home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 8: LINUX Games / Linux Cubed Series 8 - LINUX Games.iso / games / muds / pennmush.000 / pennmush-1.50-p8-linux.tar / pennmush / eval.c < prev    next >
C/C++ Source or Header  |  1993-10-22  |  105KB  |  5,011 lines

  1. /* eval.c */
  2.  
  3. #include "copyright.h"
  4.  
  5. #include <ctype.h>
  6. #include <string.h>
  7. #include <math.h>
  8. #include <sys/time.h>
  9. #include <sys/types.h>
  10.  
  11. #include "config.h"
  12. #include "db.h"
  13. #include "interface.h"
  14. #include "externs.h"
  15. #include "globals.h"
  16.  
  17. #ifdef MEM_CHECK
  18. #include "mem_check.h"
  19. #endif
  20.  
  21. #define FUNCTION(x)   \
  22.   static void x(buff, args, nargs, privs, doer) \
  23.      char *buff; \
  24.      char *args[10]; \
  25.      int nargs; \
  26.      dbref privs; \
  27.      dbref doer;
  28.  
  29. #define GLOBAL_FUN(x)  \
  30.   static void x(buff, args, nargs, privs, doer, fn_num) \
  31.      char *buff; \
  32.      char *args[10]; \
  33.      int nargs; \
  34.      dbref privs; \
  35.      dbref doer; \
  36.      int fn_num;
  37.  
  38. #ifdef FLOATING_POINTS
  39. #define aton  atof
  40. #else
  41. #define aton  atoi
  42. #endif                /* FLOATING_POINTS */
  43.  
  44. /*
  45.  * privs == player == %!
  46.  * doer == cause == %N     (enactor)
  47.  */
  48.  
  49. #define FN_REG        0
  50. #define FN_NOPARSE    1
  51.  
  52. #define MAX_GLOBAL_FNS   50
  53. #define GLOBAL_OFFSET    100
  54.  
  55. #define GF_Index(x)      (x - GLOBAL_OFFSET)
  56.  
  57. /* excessive recursion prevention */
  58. #define MAX_NEST_LEVEL  20
  59. static int recurs_lev = 0;
  60. int invok_counter = 0;
  61.  
  62. /* functions not found in this file */
  63.  
  64. extern void fun_idlesecs();  /* bsd.c */
  65. extern void fun_conn();         /* bsd.c */
  66. extern void fun_lattr();     /* attrib.c */
  67. extern void fun_lsearch();   /* wiz.c */
  68. extern void fun_lstats();    /* wiz.c */
  69. extern void fun_lwho();      /* bsd.c */
  70.  
  71. #ifdef USE_MAILER
  72. extern void fun_mail();        /* mail.c */
  73. #endif                /* USE_MAILER */
  74.  
  75. static void fun_gfun();        /* for later reference */
  76.  
  77. char rptr[10][BUFFER_LEN];    /* local registers */
  78.  
  79. #define ALPHANUM_LIST  0
  80. #define NUMERIC_LIST   1
  81. #define DBREF_LIST     2
  82.  
  83. /* -------------------------------------------------------------------------*
  84.  * Utilities.
  85.  */
  86.  
  87. dbref match_thing(player, name)
  88.     dbref player;
  89.     const char *name;
  90. {
  91.     init_match(player, name, NOTYPE);
  92.     match_everything();
  93.     return (noisy_match_result());
  94. }
  95.  
  96. #ifdef FLOATING_POINTS
  97. static void fval(buff, num)
  98.      char *buff;
  99.      double num;
  100. {
  101.     /* Copy a floating-point value into a buffer, removing trailing zeros
  102.      * and a possible ending '.'
  103.      */
  104.  
  105.     char *p;
  106.  
  107.     sprintf(buff, "%.6f", num);
  108.  
  109.     /* zap extra trailing zeros. */
  110.     if ((p = (char *) rindex(buff, '0')) == NULL)
  111.     return;
  112.     else if (*(p+1) == '\0') {
  113.     while (*p == '0')
  114.         *p-- = '\0';
  115.     }
  116.  
  117.     /* zap trailing '.' */
  118.     p = (char *) rindex(buff, '.');
  119.     if (*(p+1) == '\0')
  120.     *p = '\0';
  121. }
  122. #else
  123. #define fval(b,n)   sprintf(b, "%d", n)
  124. #endif                /* FLOATING_POINTS */
  125.  
  126.  
  127. static int ok_nargs(fname, nargs, min, max, buff)
  128.      const char *fname;
  129.      int nargs;
  130.      int min;
  131.      int max;
  132.      char *buff;
  133. {
  134.     /* Check the number of args passed to a varargs function, make sure it
  135.      * falls within the correct range.
  136.      */
  137.     
  138.     if ((nargs >= min) && (nargs <= max))
  139.     return 1;
  140.  
  141.     if (max == min+1)
  142.     sprintf(buff, "#-1 FUNCTION (%s) EXPECTS %d OR %d ARGUMENTS",
  143.         fname, min, max);
  144.     else
  145.     sprintf(buff, "#-1 FUNCTION (%s) EXPECTS BETWEEN %d AND %d ARGUMENTS",
  146.         fname, min, max);
  147.     return 0;
  148. }
  149.  
  150. static int list2arr(r, max, list)
  151.      char *r[];
  152.      int max;
  153.      char *list;
  154. {
  155.   /* chops up a list of words into an array of words. The list is
  156.    * destructively modified.
  157.    */
  158.  
  159.   char *p;
  160.   int i;
  161.  
  162.   for (i = 0, p = (char *) strtok(list, " ");
  163.        (p != NULL) && (i < max);
  164.        i++, p = (char *) strtok(NULL, " "))
  165.     r[i] = p;
  166.   return i;
  167. }
  168.  
  169. static void arr2list(r, max, list)
  170.      char *r[];
  171.      int max;
  172.      char *list;
  173. {
  174.   int i;
  175.   char *bp = list;
  176.  
  177.   safe_str(r[0], list, &bp);
  178.   for (i = 1; i < max; i++) {
  179.     safe_chr(' ', list, &bp);
  180.     safe_str(r[i], list, &bp);
  181.   }
  182.   *bp = '\0';
  183. }
  184.  
  185. static int dbnum(str)
  186.      char *str;
  187. {
  188.     if ((strlen(str) < 2) && (*str != '#'))
  189.     return 0;
  190.     else
  191.     return atoi(str+1);
  192. }
  193.  
  194. static int autodetect_list(ptrs, nptrs)
  195.      char *ptrs[];
  196.      int nptrs;
  197. {
  198.     int sort_type, i;
  199.     char *p;
  200.  
  201.     sort_type = NUMERIC_LIST;
  202.  
  203.     for (i = 0; i < nptrs; i++) {
  204.     switch (sort_type) {
  205.       case NUMERIC_LIST:
  206.         if (!is_number(ptrs[i])) {
  207.         /* If we get something non-numeric, switch to an
  208.          * alphanumeric guess, unless this is the first
  209.          * element and we have a dbref.
  210.          */
  211.         if (i == 0) {
  212.             p = ptrs[i];
  213.             if (*p++ != NUMBER_TOKEN)
  214.             return ALPHANUM_LIST;
  215.             else if (is_number(p))
  216.             sort_type = DBREF_LIST;
  217.             else
  218.             return ALPHANUM_LIST;
  219.         } else {
  220.             return ALPHANUM_LIST;
  221.         }
  222.         }
  223.         break;
  224.       case DBREF_LIST:
  225.         /* If what we get following the '#' sign isn't a number,
  226.          * we sort on alphanumeric.
  227.          */
  228.         p = ptrs[i];
  229.         if (*p++ != NUMBER_TOKEN)
  230.         return ALPHANUM_LIST;
  231.         if (!is_number(p))
  232.         return ALPHANUM_LIST;
  233.         break;
  234.       default:
  235.         return ALPHANUM_LIST;
  236.     }
  237.     }
  238.     return sort_type;
  239. }
  240.  
  241. static int get_list_type(args, nargs, type_pos, ptrs, nptrs)
  242.      char *args[];
  243.      int nargs;
  244.      int type_pos;
  245.      char *ptrs[];
  246.      int nptrs;
  247. {
  248.     if (nargs >= type_pos) {
  249.     switch (tolower(*args[type_pos - 1])) {
  250.       case 'a':
  251.         return ALPHANUM_LIST;
  252.       case 'd':
  253.         return DBREF_LIST;
  254.       case 'n':
  255.         return NUMERIC_LIST;
  256.       case '\0':
  257.         return autodetect_list(ptrs, nptrs);
  258.       default:
  259.         return ALPHANUM_LIST;
  260.     }
  261.     }
  262.     return autodetect_list(ptrs, nptrs);
  263. }
  264.  
  265. /* -------------------------------------------------------------------------*
  266.  
  267.    All function declarations follow the format
  268.  
  269.    static void fun_<name>(char *buff, char *args[10], dbref privs, dbref doer)
  270.  
  271.    All results are returned in buff. Be careful not to overflow this buffer.
  272.  
  273.    args are the arguments passed to the function. There cannot be more than
  274.    ten. They can be hacked up if necessary, but for ease in debugging, it's
  275.    probably a better idea not to.
  276.  
  277.    privs is the object executing the function.
  278.    doer is the enactor (the thing which triggered privs).
  279.  
  280.  *--------------------------------------------------------------------------*/
  281.  
  282.  
  283. /* --------------------------------------------------------------------------
  284.  * Utility functions: RAND, DIE, SECURE, SPACE, BEEP, SWITCH, EDIT,
  285.  *     ITER, ESCAPE, SQUISH
  286.  */
  287.  
  288. FUNCTION(fun_rand)
  289. {
  290.   /*
  291.    * Uses Sh'dow's random number generator, found in utils.c.  Better
  292.    * distribution than original, w/ minimal speed losses.
  293.    */
  294.   sprintf(buff, "%d", getrandom(atoi(args[0])));
  295.   
  296. }
  297.  
  298. FUNCTION(fun_die)
  299. {
  300.   int n = atoi(args[0]);
  301.   int die = atoi(args[1]);
  302.   int count;
  303.   int total = 0;
  304.  
  305.   if ((n < 1) || (n > 20)) {
  306.     strcpy(buff, "#-1 NUMBER OUT OF RANGE");
  307.     return;
  308.   }
  309.  
  310.   for (count = 0; count < n; count++)
  311.     total += getrandom(die) + 1;
  312.  
  313.   sprintf(buff, "%d", total);
  314. }
  315.  
  316. FUNCTION(fun_secure)
  317. {
  318.   /* this function smashes all occurences of "unsafe" characters in a string.
  319.    * "unsafe" characters are ( ) [ ] { } $ % , ; \
  320.    * these characters get replaced by spaces
  321.    */
  322.  
  323.   strcpy(buff, args[0]);
  324.   while (*buff) {
  325.     switch (*buff) {
  326.     case '(':
  327.     case ')':
  328.     case '[':
  329.     case ']':
  330.     case '{':
  331.     case '}':
  332.     case '$':
  333.     case '%':
  334.     case ',':
  335.     case ';':
  336.     case '\\':
  337.       *buff = ' ';
  338.       break;
  339.     }
  340.     buff++;
  341.   }
  342. }
  343.  
  344. FUNCTION(fun_escape)
  345. {
  346.   /* another function more or less right out of 2.0 code */
  347.  
  348.   char *s, *p;
  349.  
  350.   s = args[0];
  351.   p = buff;
  352.   while (*s) {
  353.     switch (*s) {
  354.     case '%':
  355.     case '\\':
  356.     case '[':
  357.     case ']':
  358.     case '{':
  359.     case '}':
  360.     case ';':
  361.       safe_chr('\\', buff, &p);
  362.     default:
  363.       if (p == buff)
  364.     safe_chr('\\', buff, &p);
  365.       safe_chr(*s, buff, &p);
  366.     }
  367.     s++;
  368.   }
  369.   *p = '\0';
  370. }
  371.  
  372. FUNCTION(fun_squish)
  373. {
  374.   /* zaps leading and trailing spaces, and reduces other spaces to a single
  375.    * space. This only applies to the literal space character, and not to
  376.    * tabs, newlines, etc.
  377.    * We do not need to check for buffer length overflows, since we're
  378.    * never going to end up with a longer string.
  379.    */
  380.  
  381.   char *bp, *tp;
  382.  
  383.   bp = buff;
  384.   for (tp = args[0]; *tp == ' '; tp++)           /* skip leading spaces */
  385.       ;
  386.  
  387.   while (*tp) {
  388.  
  389.     while (*tp && (*tp != ' '))        /* copy non-spaces */
  390.       *bp++ = *tp++;
  391.  
  392.     if (!*tp) {            /* end of string */
  393.       *bp = '\0';
  394.       return;
  395.     }
  396.  
  397.     /* we've hit a space. Copy it, then skip to the next non-space. */
  398.     *bp++ = *tp++;
  399.     while (*tp && (*tp == ' '))
  400.       tp++;
  401.   }
  402.  
  403.   /* we might have to get rid of trailing spaces. Just overwrite them with
  404.    * nulls. Make sure we're not at the beginning of the string, though.
  405.    */
  406.   if ((bp != buff) && (*(tp -1) == ' ')) {
  407.     bp--;
  408.     while ((bp != buff) && (*bp == ' '))
  409.       *bp-- = '\0';
  410.   } else {
  411.     *bp = '\0';
  412.   }
  413. }
  414.  
  415. FUNCTION(fun_space)
  416. {
  417.   char tbuf1[BUFFER_LEN];
  418.   int i;
  419.   int s = atoi(args[0]);
  420.  
  421.   if (s <= 0) {
  422.     *buff = '\0';
  423.     return;
  424.   }
  425.  
  426.   if (s > BUFFER_LEN - 3)
  427.     s = BUFFER_LEN - 3;
  428.  
  429.   for (i = 0; i < s; i++)
  430.     tbuf1[i] = ' ';
  431.   tbuf1[i] = '\0';
  432.   strcpy(buff, tbuf1);
  433. }
  434.  
  435. FUNCTION(fun_beep)
  436. {
  437.   char tbuf1[10];
  438.   int i, k;
  439.  
  440.   /* this function prints 1 to 5 beeps. The alert character '\a' is
  441.    * an ANSI C invention; non-ANSI-compliant implementations may ignore
  442.    * the '\' character and just print an 'a', or do something else nasty.
  443.    */
  444.  
  445.   k = atoi(args[0]);
  446.  
  447.   if (!Hasprivs(privs) || (k <= 0) || (k > 5)) {
  448.     strcpy(buff, "#-1 PERMISSION DENIED");
  449.     return;
  450.   }
  451.  
  452.   for (i = 0; i < k; i++)
  453.     tbuf1[i] = '\a';
  454.   tbuf1[i] = '\0';
  455.   strcpy(buff, tbuf1);
  456. }
  457.  
  458.  
  459. #define FreeArgs  \
  460.   if (mstr) { free(mstr); mstr = NULL; } \
  461.   if (curr) { free(curr); curr = NULL; } \
  462.   if (rbuf) { free(rbuf); }
  463.  
  464.  
  465. FUNCTION(fun_switch)
  466. {
  467.   /* this works a bit like the @switch command: it returns the string
  468.    * appropriate to the match. It picks the first match, like @select
  469.    * does, though.
  470.    * Args to this function are passed unparsed. Args are not evaluated
  471.    * until they are needed.
  472.    */
  473.  
  474.     int i;
  475.     char *extra[90];
  476.     int sw_args;
  477.     char *mstr, *curr, *rbuf;
  478.  
  479.     mstr = curr = rbuf = NULL;
  480.  
  481.     /* need at least two args */
  482.     if (!args[1]) {
  483.     strcpy(buff, "#-1 TOO FEW ARGUMENTS");
  484.     return;
  485.     }
  486.  
  487.     mstr = exec(privs, doer, EV_EVAL | EV_STRIP | EV_FCHECK, args[0]);
  488.  
  489.     /* try matching, return match immediately when found */
  490.  
  491.     for (i = 1; (i < 9) && args[i] && args[i + 1]; i+= 2) {
  492.  
  493.     curr = exec(privs, doer, EV_EVAL | EV_STRIP | EV_FCHECK, args[i]);
  494.  
  495.     if (local_wild_match(curr, mstr)) {
  496.         rbuf = exec(privs, doer, EV_EVAL | EV_STRIP | EV_FCHECK,
  497.             args[i+1]);
  498.         strcpy(buff, rbuf);
  499.         FreeArgs;
  500.         return;
  501.     }
  502.  
  503.     if (curr) {
  504.         free(curr);
  505.         curr = NULL;
  506.     }
  507.     }
  508.  
  509.     /* if nothing's been matched, our counter is at the default. Or, we
  510.      * could be at the tenth arg. If we are, we want to split up the
  511.      * tenth argument. It's already been parsed.
  512.      */
  513.     if ((i == 9) && args[9]) {
  514.  
  515.     sw_args = extra_arglist(args[9], extra, 90);
  516.  
  517.     if (sw_args < 2) {
  518.         /* we don't have an overflow. Just return the arg. */
  519.         rbuf = exec(privs, doer, EV_EVAL | EV_STRIP | EV_FCHECK, args[9]);
  520.         strcpy(buff, rbuf);
  521.         FreeArgs;
  522.         return;
  523.     }
  524.  
  525.     /* now we try matching just like before */
  526.     for (i = 0; (i < sw_args) && extra[i] && extra[i + 1]; i += 2) {
  527.  
  528.         curr = exec(privs, doer, EV_EVAL | EV_STRIP | EV_FCHECK, extra[i]);
  529.  
  530.         if (local_wild_match(curr, mstr)) {
  531.         rbuf = exec(privs, doer, EV_EVAL | EV_STRIP | EV_FCHECK,
  532.                 extra[i+1]);
  533.         strcpy(buff, rbuf);
  534.         FreeArgs;
  535.         return;
  536.         }
  537.             
  538.         if (curr) {
  539.         free(curr);
  540.         curr = NULL;
  541.         }
  542.     }
  543.  
  544.     /* our counter is at the default. If there is one, return it. */
  545.  
  546.     if ((i < 90) && extra[i]) {
  547.         rbuf = exec(privs, doer, EV_EVAL | EV_STRIP | EV_FCHECK, extra[i]);
  548.         strcpy(buff, rbuf);
  549.         FreeArgs;
  550.         return;
  551.     }
  552.  
  553.     } else if ((i < 10) && args[i]) {
  554.  
  555.     /* no extra arguments, just copy the default */
  556.  
  557.     rbuf = exec(privs, doer, EV_EVAL | EV_STRIP | EV_FCHECK, args[i]);
  558.     strcpy(buff, rbuf);
  559.     FreeArgs;
  560.     return;
  561.     }
  562.  
  563.     /* otherwise, return nothing */
  564.     FreeArgs;
  565.     *buff = '\0';
  566. }
  567.  
  568. #undef FreeArgs
  569.  
  570. FUNCTION(fun_edit)
  571. {
  572.   int i, len;
  573.   char *str, *f, *r;
  574.  
  575.   str = args[0];        /* complete string */ 
  576.   f = args[1];            /* find this */  
  577.   r = args[2];            /* replace it with this */
  578.  
  579.   if (!*f && !*r) {        /* check for nothing, or we'll infinite loop */
  580.     strcpy(buff, str);
  581.     return;
  582.   }
  583.  
  584.   if (!strcmp(f, "$")) {
  585.     /* append */
  586.     if (strlen(str) + strlen(r) < BUFFER_LEN) {
  587.       strcpy(buff, str);
  588.       strcat(buff, r);
  589.     } else {
  590.       strcpy(buff, "#-1 STRING TOO LONG");
  591.     }
  592.   } else if (!strcmp(f, "^")) {
  593.     /* prepend */
  594.     if (strlen(str) + strlen(r) < BUFFER_LEN) {
  595.       strcpy(buff, r);
  596.       strcat(buff, str);
  597.     } else {
  598.       strcpy(buff, "#-1 STRING TOO LONG");
  599.     }
  600.   } else {
  601.     /* find and replace */
  602.     len = strlen(f);
  603.     for (i = 0; (i < BUFFER_LEN) && *str; )
  604.       if (strncmp(f, str, len) == 0) {
  605.     if ((i + strlen(r)) < BUFFER_LEN) {
  606.       strcpy(buff + i, r);
  607.       i += strlen(r);
  608.       str += len;
  609.     } else
  610.       buff[i++] = *str++;
  611.       } else
  612.     buff[i++] = *str++;
  613.     buff[i++] = '\0';
  614.   }
  615. }
  616.  
  617. FUNCTION(fun_iter)
  618. {
  619.   /* more or less straight from the TinyMUSH 2.0 code.
  620.    * There is one big difference -- arguments to this function are UNPARSED.
  621.    */
  622.  
  623.   char *curr, *objstr, *tbuf, *result, *bp;
  624.   char *s = NULL;
  625.  
  626.   s = curr = exec(privs, doer, EV_EVAL | EV_STRIP | EV_FCHECK, args[0]);
  627.   bp = buff;
  628.  
  629.   while (curr && *curr) {
  630.     while (isspace(*curr))
  631.       curr++;
  632.     if (*curr) {
  633.       objstr = parse_to(&curr, ' ', EV_STRIP);
  634.       tbuf = replace_string("##", objstr, args[1]);
  635.       result = exec(privs, doer, EV_FCHECK, tbuf);
  636.       free(tbuf);
  637.       if (buff != bp)
  638.     safe_chr(' ', buff, &bp);
  639.       safe_str(result, buff, &bp);
  640.       free(result);
  641.     }
  642.   }
  643.   *bp = '\0';
  644.   if (s)
  645.     free(s);
  646. }
  647.  
  648. /* --------------------------------------------------------------------------
  649.  * Time functions: TIME, SECS, CONVTIME, CONVSECS
  650.  */
  651.  
  652. FUNCTION(fun_time)
  653. {
  654.   time_t tt;
  655.   tt = time((time_t *) 0);
  656.   sprintf(buff, "%s", ctime(&tt));
  657.   buff[strlen(buff)-1] = '\0';
  658. }
  659.  
  660. FUNCTION(fun_secs)
  661. {
  662.   time_t tt;
  663.   time(&tt);
  664.   strcpy(buff, tprintf("%d", tt));
  665. }
  666.  
  667. FUNCTION(fun_convsecs)
  668. {
  669.   /* converts seconds to a time string */
  670.  
  671.   time_t tt;
  672.   tt = atol(args[0]);
  673.   sprintf(buff, "%s", ctime(&tt));
  674.   buff[strlen(buff)-1] = '\0';
  675. }
  676.  
  677. static const char *month_table[] =
  678. {
  679.   "Jan",
  680.   "Feb",
  681.   "Mar",
  682.   "Apr",
  683.   "May",
  684.   "Jun",
  685.   "Jul",
  686.   "Aug",
  687.   "Sep",
  688.   "Oct",
  689.   "Nov",
  690.   "Dec",
  691. };
  692.  
  693. int do_convtime(str, ttm)
  694.      char *str;
  695.      struct tm *ttm;
  696. {
  697.   /* converts time string to a struct tm. Returns 1 on success, 0 on fail.
  698.    * Time string format is always 24 characters long, in format
  699.    * Ddd Mmm DD HH:MM:SS YYYY
  700.    */
  701.  
  702.   char buf[32];
  703.   char *p, *q;
  704.   int i;
  705.  
  706.   if (strlen(str) != 24)
  707.     return 0;
  708.  
  709.   /* do not hack up the string we were passed */
  710.   strcpy(buf, str);
  711.  
  712.   /* move over the day of week and truncate. Will always be 3 chars.
  713.    * we don't need this, so we can ignore it.
  714.    */
  715.   p = (char *) index(buf, ' ');
  716.   if (p)
  717.     *p++ = '\0';
  718.   if (strlen(buf) != 3)
  719.     return 0;
  720.  
  721.   /* get the month (3 chars), and convert it to a number */
  722.   q = (char *) index(p, ' ');
  723.   if (q)
  724.     *q++ = '\0';
  725.   if (strlen(p) != 3)
  726.     return 0;
  727.   else {
  728.     for (i = 0; (i < 12) && strcmp(month_table[i], p); i++)
  729.       ;
  730.     if (i == 12)        /* not found */
  731.       return 0;
  732.     else
  733.       ttm->tm_mon = i;
  734.   }
  735.  
  736.   /* get the day of month */
  737.   p = q;
  738.   while (isspace(*p))        /* skip leading space */
  739.     p++;
  740.   q = (char *) index(p, ' ');
  741.   if (q)
  742.     *q++ = '\0';
  743.   ttm->tm_mday = atoi(p);
  744.  
  745.   /* get hours */
  746.   p = (char *) index(q, ':');
  747.   if (p)
  748.     *p++ = '\0';
  749.   ttm->tm_hour = atoi(q);
  750.  
  751.   /* get minutes */
  752.   q = (char *) index(p, ':');
  753.   if (q)
  754.     *q++ = '\0';
  755.   ttm->tm_min = atoi(p);
  756.  
  757.   /* get seconds */
  758.   p = (char *) index(q, ' ');
  759.   if (p)
  760.     *p++ = '\0';
  761.   ttm->tm_sec = atoi(q);
  762.  
  763.   /* get year */
  764.   ttm->tm_year = atoi(p) - 1900;
  765.  
  766.   return 1;
  767. }
  768.  
  769. FUNCTION(fun_convtime)
  770. {
  771.   /* converts time string to seconds */
  772.   
  773.   struct tm *ttm;
  774.  
  775.   ttm = (struct tm *) malloc(sizeof(struct tm));
  776.   if (do_convtime(args[0], ttm)) {
  777. #ifdef SUN_OS
  778.     sprintf(buff, "%d", timelocal(ttm));
  779. #else
  780.     sprintf(buff, "%d", mktime(ttm));
  781. #endif                /* SUN_OS */
  782.   } else {
  783.     strcpy(buff, "-1");
  784.   }
  785.   free(ttm);
  786. }
  787.  
  788. /* --------------------------------------------------------------------------
  789.  * Attribute functions: GET, XGET, V, S, UFUN, ZFUN
  790.  */
  791.  
  792.  
  793. char *do_get_attrib(player, thing, attrib)
  794.      dbref player;
  795.      dbref thing;
  796.      char *attrib;
  797. {
  798.   ATTR *a;
  799.  
  800.   a = atr_get(thing, upcasestr(attrib));
  801.   if (!a) {
  802.     return ((char *) "");     /* used to be #-1 NO SUCH ATTRIBUTE */
  803.   } else {
  804.     if (Can_Read_Attr(player, thing, a)) {
  805.       if(strlen(uncompress(a->value)) < BUFFER_LEN)
  806.     return ((char *) uncompress(a->value));
  807.       else
  808.     return ((char *) "#-1 ATTRIBUTE LENGTH TOO LONG");
  809.     } else
  810.       return ((char *) "#-1 NO PERMISSION TO GET ATTRIBUTE");
  811.   }
  812.   return ((char *) "");        /* NOTREACHED */
  813. }
  814.  
  815. FUNCTION(fun_get)
  816. {
  817.   dbref thing;
  818.   char *s;
  819.   char tbuf1[BUFFER_LEN];
  820.  
  821.   strcpy(tbuf1, args[0]);
  822.   for(s = tbuf1; *s && (*s != '/'); s++);
  823.   if(!*s) {
  824.     strcpy(buff, "#-1 BAD ARGUMENT FORMAT TO GET");
  825.     return;
  826.   }
  827.   *s++ = 0;
  828.   thing = match_thing(privs, tbuf1);
  829.   if(thing == NOTHING) {
  830.     strcpy(buff, "#-1 NO SUCH OBJECT VISIBLE");
  831.     return;
  832.   }
  833.  
  834.   if(*s == '_') s++;
  835.   if(!*s) {
  836.     strcpy(buff, "#-1 BAD ARGUMENT FORMAT TO GET");
  837.     return;
  838.   }
  839.  
  840.   strcpy(buff, do_get_attrib(privs, thing, s));
  841. }
  842.  
  843. /* Functions like get, but uses the standard way of passing arguments */
  844. /* to a function, and thus doesn't choke on nested functions within.  */
  845.  
  846. FUNCTION(fun_xget)
  847. {
  848.   dbref thing;
  849.  
  850.   thing = match_thing(privs, args[0]);
  851.   if (thing == NOTHING) {
  852.     strcpy(buff, "#-1 NO SUCH OBJECT VISIBLE");
  853.     return;
  854.   }
  855.  
  856.   strcpy(buff, do_get_attrib(privs, thing, args[1]));
  857. }
  858.  
  859. FUNCTION(fun_eval)
  860. {
  861.   /* like xget, except pronoun substitution is done */
  862.  
  863.   dbref thing;
  864.   char tbuf1[BUFFER_LEN];
  865.   char *tbuf2;
  866.  
  867.   thing = match_thing(privs, args[0]);
  868.   if (thing == NOTHING) {
  869.     strcpy(buff, "#-1 NO SUCH OBJECT VISIBLE");
  870.     return;
  871.   }
  872.  
  873.   strcpy(tbuf1, do_get_attrib(privs, thing, args[1]));
  874.   tbuf2 = exec(thing, privs, 0, tbuf1);
  875.   strcpy(buff, tbuf2);
  876.   free(tbuf2);
  877. #ifdef MEM_CHECK
  878.   del_check("exec.buff");
  879. #endif
  880. }
  881.  
  882. FUNCTION(fun_v)
  883. {
  884.   /* handle 0-9, va-vz, n, l, # */
  885.  
  886.   int c;
  887.  
  888.   switch (c = args[0][0]) {
  889.     case '0':
  890.     case '1':
  891.     case '2':
  892.     case '3':
  893.     case '4':
  894.     case '5':
  895.     case '6':
  896.     case '7':
  897.     case '8':
  898.     case '9':
  899.       if (!wptr[c - '0']) {
  900.     buff[0] = '\0';
  901.     return;
  902.       }
  903.       if(strlen(wptr[c - '0']) < BUFFER_LEN)
  904.         strcpy(buff, wptr[c - '0']);
  905.       else
  906.     buff[0] = '\0';
  907.       break;
  908.     case 'n':
  909.     case 'N':
  910.       if(args[0][1]) {
  911.     strcpy(buff, do_get_attrib(privs, privs, args[0]));
  912.       } else
  913.         strcpy(buff, Name(doer));
  914.       break;
  915.     case '#':
  916.       sprintf(buff, "#%d", doer);
  917.       break;
  918.     case 'l': case 'L':
  919.       if(args[0][1]) {
  920.     strcpy(buff, do_get_attrib(privs, privs, args[0]));
  921.       } else
  922.     /*
  923.      * giving the location does not violate security, since the object
  924.      * is the enactor.
  925.      */
  926.     sprintf(buff, "#%d", Location(doer));
  927.       break;
  928.       /* objects # */
  929.     case '!':
  930.       sprintf(buff, "#%d", privs);
  931.       break;
  932.     default:
  933.       strcpy(buff, do_get_attrib(privs, privs, args[0]));
  934.     }
  935. }
  936.  
  937.  
  938. FUNCTION(fun_s)
  939. {
  940.   char *s;
  941.  
  942.   s = exec(privs, doer, 0, args[0]);
  943.   strcpy(buff, s);
  944.   free(s);
  945. #ifdef MEM_CHECK
  946.   del_check("exec.buff");
  947. #endif
  948. }
  949.  
  950. static void do_userfn(player, cause, obj, attrib, args, buff, flag)
  951.      dbref player, cause, obj;
  952.      ATTR *attrib;
  953.      char *args[10];
  954.      char *buff;
  955.      int flag;            /* 0 if ufun, 1 if gfun */
  956. {
  957.   int a;
  958.   char *result;
  959.   char *tptr[10];
  960.   char tbuf1[BUFFER_LEN];
  961.   char *bp = tbuf1;
  962.  
  963.   if (!attrib) {
  964.     strcpy(buff, "#-1 NO SUCH USER FUNCTION");
  965.     return;
  966.   }
  967.  
  968.   if ((flag == 0) && !Can_Read_Attr(player, obj, attrib)) {
  969.     strcpy(buff, "#-1 NO PERMISSION TO GET ATTRIBUTE");
  970.     return;
  971.   }
  972.  
  973.   safe_str(uncompress(attrib->value), tbuf1, &bp);
  974.   *bp = '\0';
  975.  
  976.   /* save our stack */
  977.   for (a = 0; a < 10; a++)
  978.     tptr[a] = wptr[a];
  979.  
  980.   /* copy the appropriate args into the stack */
  981.   if (flag == 0) {
  982.     for (a = 1; a < 10; a++)
  983.       wptr[a - 1] = args[a];
  984.     wptr[9] = NULL;        /* sorry, can't have more than 9 args */
  985.   } else {
  986.     for (a = 0; a < 10; a++)
  987.       wptr[a] = args[a];
  988.   }
  989.  
  990. #ifdef NEVER
  991.   /* now find the value of the function with the new stack. We pass
  992.    * player instead of cause so that we have a way of checking to see
  993.    * if the calling object has a right to any information that may be
  994.    * retrieved. Therefore, if anything below the top-level U() needs
  995.    * the enactor, it must pass it as a parameter.
  996.    */
  997.   result = exec(obj, player, 0, tbuf1);
  998. #else
  999.   result = exec(obj, cause, 0, tbuf1);
  1000. #endif
  1001.  
  1002.   /* restore the stack */
  1003.   for (a = 0; a < 10; a++)
  1004.     wptr[a] = tptr[a];
  1005.  
  1006.   /* copy and free */
  1007.   strcpy(buff, result);
  1008.   free(result);
  1009. #ifdef MEM_CHECK
  1010.   del_check("exec.buff");
  1011. #endif
  1012. }
  1013.  
  1014. FUNCTION(fun_ufun)
  1015. {
  1016.   ATTR *attrib;
  1017.   dbref obj;
  1018.  
  1019.   /* find the user function attribute */
  1020.  
  1021.   if (!args[0] || !*args[0]) {
  1022.     strcpy(buff, "#-1 NOT FOUND");
  1023.     return;
  1024.   }
  1025.  
  1026.   parse_attrib(privs, args[0], &obj, &attrib);
  1027.   
  1028.   do_userfn(privs, doer, obj, attrib, args, buff, 0);
  1029. }
  1030.  
  1031. FUNCTION(fun_zfun)
  1032. {
  1033.   int a;
  1034.   char *tptr[10];
  1035.   ATTR *attrib;
  1036.   char tbuf1[BUFFER_LEN];
  1037.   char *result;
  1038.  
  1039.   dbref zone = Zone(privs);
  1040.  
  1041.   if (zone == NOTHING) {
  1042.     strcpy(buff, "#-1 INVALID ZONE");
  1043.     return;
  1044.   }
  1045.  
  1046.   /* find the user function attribute */
  1047.   attrib = atr_get(zone, upcasestr(args[0]));
  1048.   if (!attrib) {
  1049.     strcpy(buff, "#-1 NO SUCH USER FUNCTION");
  1050.     return;
  1051.   }
  1052.  
  1053.   if (!Can_Read_Attr(privs, zone, attrib)) {
  1054.     strcpy(buff, "#-1 NO PERMISSION TO GET ATTRIBUTE");
  1055.     return;
  1056.   }
  1057.  
  1058.   if (strlen(uncompress(attrib->value)) < BUFFER_LEN)
  1059.     strcpy(tbuf1, uncompress(attrib->value));
  1060.   else {
  1061.     strcpy(buff, "#-1 USER FUNCTION TOO LONG");
  1062.     return;
  1063.   }
  1064.  
  1065.   /* save our stack */
  1066.   for (a = 0; a < 10; a++)
  1067.     tptr[a] = wptr[a];
  1068.  
  1069.   /* copy the appropriate args into the stack */
  1070.   for (a = 1; a < 10; a++)
  1071.     wptr[a - 1] = args[a];
  1072.   wptr[9] = NULL;        /* sorry, can't have more than 9 args */
  1073.  
  1074.   /* now find the value of the function with the new stack */
  1075.   result = exec(zone, privs, 0, tbuf1);
  1076.  
  1077.   /* restore the stack */
  1078.   for (a = 0; a < 10; a++)
  1079.     wptr[a] = tptr[a];
  1080.  
  1081.   /* copy and free */
  1082.   strcpy(buff, result);
  1083.   free(result);
  1084. #ifdef MEM_CHECK
  1085.   del_check("exec.buff");
  1086. #endif
  1087. }
  1088.  
  1089. /* --------------------------------------------------------------------------
  1090.  * Local registers: SETQ, R
  1091.  */
  1092.  
  1093.  
  1094. FUNCTION(fun_setq)
  1095. {
  1096.   /* sets a variable into a local register */
  1097.  
  1098.   int r = atoi(args[0]);
  1099.   if ((r < 0) || (r > 9)) {
  1100.     strcpy(buff, "#-1 REGISTER OUT OF RANGE");
  1101.     return;
  1102.   }
  1103.  
  1104.   strcpy(rptr[r], args[1]);
  1105.   *buff = '\0';
  1106. }
  1107.  
  1108. FUNCTION(fun_r)
  1109. {
  1110.   /* returns a local register */
  1111.  
  1112.   int r = atoi(args[0]);
  1113.   if ((r < 0) || (r > 9)) {
  1114.     strcpy(buff, "#-1 REGISTER OUT OF RANGE");
  1115.     return;
  1116.   }
  1117.  
  1118.   strcpy(buff, rptr[r]);
  1119. }
  1120.  
  1121. /* --------------------------------------------------------------------------
  1122.  * High-order functions: FILTER, FOLD
  1123.  */
  1124.  
  1125. FUNCTION(fun_fold)
  1126. {
  1127.   /* iteratively evaluates an attribute with a list of arguments and
  1128.    * optional base case. With no base case, the first list element is
  1129.    * passed as %0, and the second as %1. The attribute is then evaluated
  1130.    * with these args. The result is then used as %0, and the next arg as
  1131.    * %1. Repeat until no elements are left in the list. The base case 
  1132.    * can provide a starting point.
  1133.    */
  1134.  
  1135.   dbref thing;
  1136.   ATTR *attrib;
  1137.   char abuf[BUFFER_LEN], rstore[BUFFER_LEN];
  1138.   char *asave, *result, *bp, *cp;
  1139.   char *tptr[10];
  1140.   int i;
  1141.  
  1142.   /* check our argument count */
  1143.   if (!ok_nargs("FOLD", nargs, 2, 3, buff))
  1144.       return;
  1145.  
  1146.   /* find our object and attribute */
  1147.   parse_attrib(privs, args[0], &thing, &attrib);
  1148.   if (!GoodObject(thing) || !attrib) {
  1149.     strcpy(buff, "#-1 NOT FOUND");
  1150.     return;
  1151.   }
  1152.   if (!Can_Read_Attr(privs, thing, attrib)) {
  1153.     strcpy(buff, "#-1 NO PERMISSION TO GET ATTRIBUTE");
  1154.     return;
  1155.   }
  1156.   asave = safe_uncompress(attrib->value);
  1157.   
  1158.   cp = args[1];
  1159.   bp = buff;
  1160.   strcpy(abuf, asave);        /* save attribute text */
  1161.  
  1162.   /* save our stack */
  1163.   for (i = 0; i < 10; i++)
  1164.     tptr[i] = wptr[i];
  1165.  
  1166.   /* handle the first case */
  1167.   if (nargs == 3) {
  1168.     wptr[0] = args[2];
  1169.     wptr[1] = parse_to(&cp, ' ', 0);
  1170.   } else {
  1171.     wptr[0] = parse_to(&cp, ' ', 0);
  1172.     wptr[1] = parse_to(&cp, ' ', 0);
  1173.   }
  1174.   result = exec(privs, doer, EV_STRIP | EV_FCHECK | EV_EVAL, abuf);
  1175.  
  1176.   strcpy(rstore, result);
  1177.   free(result);
  1178.   
  1179.   /* handle the rest of the cases */
  1180.   while (cp && *cp) {
  1181.     while (*cp == ' ')        /* do not clobber tabs and newlines */
  1182.       cp++;
  1183.     if (*cp) {
  1184.       wptr[0] = rstore;
  1185.       wptr[1] = parse_to(&cp, ' ', 0);
  1186.       strcpy(abuf, asave);
  1187.       result = exec(privs, doer, EV_STRIP | EV_FCHECK | EV_EVAL, abuf);
  1188.       strcpy(rstore, result);
  1189.       free(result);
  1190.     }
  1191.   }
  1192.   safe_str(rstore, buff, &bp);
  1193.   *bp = '\0';
  1194.  
  1195.   free(asave);
  1196.  
  1197.   for (i = 0; i < 10; i++)
  1198.     wptr[i] = tptr[i];
  1199. }
  1200.  
  1201. FUNCTION(fun_filter)
  1202. {
  1203.   /* take a user-def function and a list, and return only those elements
  1204.    * of the list for which the function evaluates to 1.
  1205.    */
  1206.  
  1207.   dbref thing;
  1208.   ATTR *attrib;
  1209.   char *result, *bp, *cp, *asave;
  1210.   char *tptr[10];
  1211.   char abuf[BUFFER_LEN];
  1212.   int i;
  1213.  
  1214.   /* find our object and attribute */
  1215.   parse_attrib(privs, args[0], &thing, &attrib);
  1216.   if (!GoodObject(thing) || !attrib) {
  1217.     strcpy(buff, "#-1 NOT FOUND");
  1218.     return;
  1219.   }
  1220.   if (!Can_Read_Attr(privs, thing, attrib)) {
  1221.     strcpy(buff, "#-1 NO PERMISSION TO GET ATTRIBUTE");
  1222.     return;
  1223.   }
  1224.   asave = safe_uncompress(attrib->value);
  1225.  
  1226.   for (i = 0; i < 10; i++)
  1227.     tptr[i] = wptr[i];
  1228.  
  1229.   cp = args[1];
  1230.   bp = buff;
  1231.   while (cp && *cp) {
  1232.     while (*cp == ' ')
  1233.       cp++;
  1234.     if (*cp) {
  1235.       wptr[0] = parse_to(&cp, ' ', 0);
  1236.       strcpy(abuf, asave);
  1237.       result = exec(privs, doer, EV_STRIP | EV_FCHECK | EV_EVAL, abuf);
  1238.       if (*result == '1') {
  1239.     if (bp != buff)
  1240.       safe_chr(' ', buff, &bp);
  1241.     safe_str(wptr[0], buff, &bp);
  1242.       }
  1243.       free(result);
  1244.     }
  1245.   }
  1246.   *bp = '\0';
  1247.  
  1248.   free(asave);
  1249.  
  1250.   for (i = 0; i < 10; i++)
  1251.     wptr[i] = tptr[i];
  1252. }
  1253.  
  1254. /* --------------------------------------------------------------------------
  1255.  * Number-related functions:  LNUM, ISNUM.
  1256.  */
  1257.  
  1258. FUNCTION(fun_isnum)
  1259. {
  1260.   /* trivial little function, returns 0 or 1 depending on whether
  1261.    * or not the string is a number. No overflow checks needed.
  1262.    */
  1263.   sprintf(buff, "%d", is_number(args[0]));
  1264. }
  1265.  
  1266. FUNCTION(fun_lnum)
  1267. {
  1268.   char *bp;
  1269.   char tbuf1[8];
  1270.   int i, x;
  1271.   int done = 0;
  1272.  
  1273.   x = atoi(args[0]);
  1274.  
  1275.   if (x < 0) {
  1276.     strcpy(buff, "#-1 NUMBER OUT OF RANGE");
  1277.     return;
  1278.   }
  1279.  
  1280.   bp = buff;
  1281.   safe_chr('0', buff, &bp);
  1282.   for (i = 1; i < x && !done; i++) {
  1283.     sprintf(tbuf1, " %d", i);
  1284.     done = safe_str(tbuf1, buff, &bp);
  1285.   }
  1286.   *bp = '\0';
  1287. }
  1288.  
  1289. /* --------------------------------------------------------------------------
  1290.  * Arithmetic on both integers and floating points: ADD, SUB, MUL, GT, GTE,
  1291.  *   LT, LTE, EQ, NEQ, MAX, MIN, SIGN.
  1292.  */
  1293.  
  1294. FUNCTION(fun_add)
  1295. {
  1296.     if (is_number(args[0]) && is_number(args[1]))
  1297.     fval(buff, aton(args[0]) + aton(args[1]));
  1298.     else
  1299.     strcpy(buff, "#-1 ARGUMENTS MUST BE NUMBERS");
  1300. }
  1301.  
  1302. FUNCTION(fun_sub)
  1303. {
  1304.     if (is_number(args[0]) && is_number(args[1]))
  1305.     fval(buff, aton(args[0]) - aton(args[1]));
  1306.     else
  1307.     strcpy(buff, "#-1 ARGUMENTS MUST BE NUMBERS");
  1308. }
  1309.  
  1310. FUNCTION(fun_mul)
  1311. {
  1312.     if (is_number(args[0]) && is_number(args[1]))
  1313.     fval(buff, aton(args[0]) * aton(args[1]));
  1314.     else
  1315.     strcpy(buff, "#-1 ARGUMENTS MUST BE NUMBERS");
  1316. }
  1317.  
  1318. FUNCTION(fun_gt)
  1319. {
  1320.     sprintf(buff, "%d", (aton(args[0]) > aton(args[1])));
  1321. }
  1322.  
  1323. FUNCTION(fun_gte)
  1324. {
  1325.     sprintf(buff, "%d", (aton(args[0]) >= aton(args[1])));
  1326. }
  1327.  
  1328. FUNCTION(fun_lt)
  1329. {
  1330.     sprintf(buff, "%d", (aton(args[0]) < aton(args[1])));
  1331. }
  1332.  
  1333. FUNCTION(fun_lte)
  1334. {
  1335.     sprintf(buff, "%d", (aton(args[0]) <= aton(args[1])));
  1336. }
  1337.  
  1338. FUNCTION(fun_eq)
  1339. {
  1340.     sprintf(buff, "%d", (aton(args[0]) == aton(args[1])));
  1341. }
  1342.  
  1343. FUNCTION(fun_neq)
  1344. {
  1345.     sprintf(buff, "%d", (aton(args[0]) != aton(args[1])));
  1346. }
  1347.  
  1348. FUNCTION(fun_max)
  1349. {
  1350.     double max;
  1351.     int i = 1;
  1352.     if (!args[0]) {
  1353.     strcpy(buff, "#-1 TOO FEW ARGUMENTS");
  1354.     return;
  1355.     } else
  1356.     max = aton(args[0]);
  1357.     while (i < 10 && args[i]) {
  1358.     max = (aton(args[i]) > max) ? aton(args[i]) : max;
  1359.     i++;
  1360.     }
  1361.     fval(buff, max);
  1362. }
  1363.  
  1364. FUNCTION(fun_min)
  1365. {
  1366.     double min;
  1367.     int i = 1;
  1368.     if (!args[0]) {
  1369.     strcpy(buff, "#-1 TOO FEW ARGUMENTS");
  1370.     return;
  1371.     } else 
  1372.     min = aton(args[0]);
  1373.     while (i < 10 && args[i]) {
  1374.     min = (aton(args[i]) < min) ? aton(args[i]) : min;
  1375.     i++;
  1376.     }
  1377.     fval(buff, min);
  1378. }
  1379.  
  1380.  
  1381. FUNCTION(fun_sign)
  1382. {
  1383.     int x = aton(args[0]);
  1384.     if (x == 0)
  1385.     strcpy(buff, "0");
  1386.     else if (x > 0)
  1387.     strcpy(buff, "1");
  1388.     else
  1389.     strcpy(buff, "-1");
  1390. }
  1391.  
  1392. /* --------------------------------------------------------------------------
  1393.  * Integer-only functions: TRUNC, DIV, MOD, ABS, DIST2D, DIST3D.
  1394.  */
  1395.  
  1396. FUNCTION(fun_trunc)
  1397. {
  1398.     sprintf(buff, "%d", atoi(args[0]));
  1399. }
  1400.  
  1401. FUNCTION(fun_div)
  1402. {
  1403.     int bot;
  1404.     
  1405.     if (is_number(args[0]) && is_number(args[1])) {
  1406.     bot = atoi(args[1]);
  1407.     if (bot == 0) {
  1408.         strcpy(buff, "#-1 DIVISION BY ZERO");
  1409.     } else {
  1410.         sprintf(buff, "%d", atoi(args[0]) / bot);
  1411.     }
  1412.     } else {
  1413.     strcpy(buff, "#-1 ARGUMENTS MUST BE NUMBERS");
  1414.     }
  1415. }
  1416.  
  1417. FUNCTION(fun_mod)
  1418. {
  1419.     int bot;
  1420.     
  1421.     if (is_number(args[0]) && is_number(args[1])) {
  1422.     bot = atoi(args[1]);
  1423.     if (bot == 0) {
  1424.         strcpy(buff, "#-1 DIVISION BY ZERO");
  1425.     } else {
  1426.         sprintf(buff, "%d", atoi(args[0]) % bot);
  1427.     }
  1428.     } else {
  1429.     strcpy(buff, "#-1 ARGUMENTS MUST BE NUMBERS");
  1430.     }
  1431. }
  1432.  
  1433. FUNCTION(fun_abs)
  1434. {
  1435.   sprintf(buff, "%d", abs(atoi(args[0])));
  1436. }
  1437.  
  1438. /* this function and dist3d are taken from the 2.0 code */
  1439.  
  1440. FUNCTION(fun_dist2d)
  1441. {
  1442.     int d;
  1443.     double r;
  1444.     d = atoi(args[0]) - atoi(args[2]);
  1445.     r = (double) (d * d);
  1446.     d = atoi(args[1]) - atoi(args[3]);
  1447.     r += (double) (d * d);
  1448.     d = (int) (sqrt(r) + 0.5);
  1449.     sprintf(buff, "%d", d);
  1450. }
  1451.  
  1452. FUNCTION(fun_dist3d)
  1453. {
  1454.     int d;
  1455.     double r;
  1456.     d = atoi(args[0]) - atoi(args[3]);
  1457.     r = (double) (d * d);
  1458.     d = atoi(args[1]) - atoi(args[4]);
  1459.     r += (double) (d * d);
  1460.     d = atoi(args[2]) - atoi(args[5]);
  1461.     r += (double) (d * d);
  1462.     d = (int) (sqrt(r) + 0.5);
  1463.     sprintf(buff, "%d", d);
  1464. }
  1465.  
  1466. /* --------------------------------------------------------------------------
  1467.  * Floating-point-only functions: FDIV, FLOOR, CEIL, ROUND, PI, E,
  1468.  *   SIN, ASIN, COS, ACOS, TAN, ATAN, EXP, POWER, LN, LOG
  1469.  */
  1470.  
  1471. #ifdef FLOATING_POINTS
  1472.  
  1473. FUNCTION(fun_fdiv)
  1474. {
  1475.     double bot;
  1476.     
  1477.     if (is_number(args[0]) && is_number(args[1])) {
  1478.     bot = atof(args[1]);
  1479.     if (bot == 0) {
  1480.         strcpy(buff, "#-1 DIVISION BY ZERO");
  1481.     } else {
  1482.         fval(buff, atof(args[0]) / bot);
  1483.     }
  1484.     } else {
  1485.     strcpy(buff, "#-1 ARGUMENTS MUST BE NUMBERS");
  1486.     }
  1487. }
  1488.  
  1489. FUNCTION(fun_floor)
  1490. {
  1491.     sprintf(buff, "%.0f", floor(atof(args[0])));
  1492. }
  1493.  
  1494. FUNCTION(fun_ceil)
  1495. {
  1496.     sprintf(buff, "%.0f", ceil(atof(args[0])));
  1497. }
  1498.  
  1499. FUNCTION(fun_round)
  1500. {
  1501.     const char *fstr;
  1502.  
  1503.     switch (atoi(args[1])) {
  1504.       case 1:        fstr = "%.1f"; break;
  1505.       case 2:        fstr = "%.2f"; break;
  1506.       case 3:        fstr = "%.3f"; break;
  1507.       case 4:        fstr = "%.4f"; break;
  1508.       case 5:        fstr = "%.5f"; break;
  1509.       case 6:        fstr = "%.6f"; break;
  1510.       default:            fstr = "%.0f"; break;
  1511.     }
  1512.     sprintf(buff, fstr, atof(args[0]));
  1513.  
  1514.     /* Handle the bizarre "-0" sprintf problem. */
  1515.     if (!strcmp(buff, "-0")) {
  1516.     strcpy(buff, "0");
  1517.     }
  1518. }
  1519.  
  1520. FUNCTION(fun_pi)
  1521. {
  1522.     strcpy(buff, "3.141592");
  1523. }
  1524.  
  1525. FUNCTION(fun_e)
  1526. {
  1527.     strcpy(buff, "2.718281");
  1528. }
  1529.  
  1530. FUNCTION(fun_sin)
  1531. {
  1532.     fval(buff, sin(atof(args[0])));
  1533. }
  1534.  
  1535. FUNCTION(fun_asin)
  1536. {
  1537.     double n = atof(args[0]);
  1538.  
  1539.     if ((n < -1) || (n > 1))
  1540.     strcpy(buff, "#-1 OUT OF RANGE");
  1541.     else
  1542.     fval(buff, asin(n));
  1543. }
  1544.  
  1545. FUNCTION(fun_cos)
  1546. {
  1547.     fval(buff, cos(atof(args[0])));
  1548. }
  1549.  
  1550. FUNCTION(fun_acos)
  1551. {
  1552.     double n = atof(args[0]);
  1553.     
  1554.     if ((n < -1) || (n > 1))
  1555.     strcpy(buff, "#-1 OUT OF RANGE");
  1556.     else
  1557.     fval(buff, acos(n));
  1558. }
  1559.  
  1560. FUNCTION(fun_tan)
  1561. {
  1562.     fval(buff, tan(atof(args[0])));
  1563. }
  1564.  
  1565. FUNCTION(fun_atan)
  1566. {
  1567.     fval(buff, atan(atof(args[0])));
  1568. }
  1569.  
  1570. FUNCTION(fun_exp)
  1571. {
  1572.     fval(buff, exp(atof(args[0])));
  1573. }
  1574.  
  1575. FUNCTION(fun_power)
  1576. {
  1577.     double n;
  1578.  
  1579.     if ((n = atof(args[0])) < 0)
  1580.     strcpy(buff, "#-1 POWER OF NEGATIVE");
  1581.     else
  1582.     fval(buff, pow(n, atof(args[1])));
  1583. }
  1584.  
  1585. FUNCTION(fun_ln)
  1586. {
  1587.     double n;
  1588.  
  1589.     if ((n = atof(args[0])) > 0)
  1590.     fval(buff, log(n));
  1591.     else
  1592.     strcpy(buff, "#-1 OUT OF RANGE");
  1593. }
  1594.  
  1595. FUNCTION(fun_log)
  1596. {
  1597.     double n;
  1598.  
  1599.     if ((n = atof(args[0])) > 0)
  1600.     fval(buff, log10(n));
  1601.     else
  1602.     strcpy(buff, "#-1 OUT OF RANGE");
  1603. }
  1604.  
  1605. #endif                /* FLOATING_POINTS */
  1606.  
  1607. /* --------------------------------------------------------------------------
  1608.  * String functions: FIRST, REST, STRLEN, COMP, POS, MID, EXTRACT, WORDPOS,
  1609.  *   MATCH, CAT, REMOVE, MEMBER, FLIP, UCSTR, LCSTR, WORDS, BEFORE, AFTER,
  1610.  *   STRCAT, REVWORDS, MERGE, SPLICE, REPEAT, GREP, LJUST, RJUST
  1611.  */
  1612.  
  1613. FUNCTION(fun_first)
  1614. {
  1615. /* read first word from a string */
  1616.  
  1617.   char *s, *b;
  1618.  
  1619.   b = skip_space(args[0]);
  1620.   s = seek_char(b, ' ');
  1621.   if (s)
  1622.     *s = '\0';
  1623.  
  1624.   if(strlen(b) < BUFFER_LEN)
  1625.     strcpy(buff, b);
  1626.   else
  1627.     buff[0] = '\0';
  1628. }
  1629.  
  1630. FUNCTION(fun_rest)
  1631. {
  1632.   char *s;
  1633.  
  1634.   /* skip leading space */
  1635.   s = skip_space(args[0]);
  1636.  
  1637.   /* skip first word */
  1638.   s = seek_char(s, ' ');
  1639.  
  1640.   /* skip leading space */
  1641.   s = skip_space(s);
  1642.  
  1643.   if(strlen(s) < BUFFER_LEN)
  1644.     strcpy(buff, s);
  1645.   else
  1646.     buff[0] = '\0';
  1647. }
  1648.  
  1649. FUNCTION(fun_strlen)
  1650. {
  1651.   sprintf(buff, "%d", strlen(args[0]));
  1652. }
  1653.  
  1654. FUNCTION(fun_mid)
  1655. {
  1656.   int l = atoi(args[1]), len = atoi(args[2]);
  1657.   if ((l < 0) || (len < 0) || ((len + l) > BUFFER_LEN)) {
  1658.     strcpy(buff, "#-1 OUT OF RANGE");
  1659.     return;
  1660.   }
  1661.   if (l < strlen(args[0]))
  1662.     strcpy(buff, args[0] + l);
  1663.   else
  1664.     *buff = 0;
  1665.   buff[len] = 0;
  1666. }
  1667.  
  1668. FUNCTION(fun_comp)
  1669. {
  1670.   int x;
  1671.   x = strcmp(args[0], args[1]);
  1672.   if (x > 0)
  1673.     strcpy(buff, "1");
  1674.   else if (x < 0)
  1675.     strcpy(buff, "-1");
  1676.   else
  1677.     strcpy(buff, "0");
  1678. }
  1679.  
  1680. FUNCTION(fun_pos)
  1681. {
  1682.   int i = 1;
  1683.   char *t, *u, *s = args[1];
  1684.   while (*s) {
  1685.     u = s;
  1686.     t = args[0];
  1687.     while (*t && *t == *u)
  1688.       ++t, ++u;
  1689.     if (*t == '\0') {
  1690.       sprintf(buff, "%d", i);
  1691.       return;
  1692.     }
  1693.     ++i, ++s;
  1694.   }
  1695.   strcpy(buff, "#-1");
  1696.   return;
  1697. }
  1698.  
  1699. FUNCTION(fun_match)
  1700. {
  1701.   /* compares two strings with possible wildcards, returns the
  1702.    * word position of the match. Based on the 2.0 version of this
  1703.    * function.
  1704.    */
  1705.  
  1706.   int wcount = 1;
  1707.   char *s = args[0];
  1708.   char *r;
  1709.  
  1710.   do {
  1711.  
  1712.     r = skip_space(s);
  1713.     s = seek_char(r, ' ');
  1714.     if (*s)
  1715.       *s++ = '\0';
  1716.  
  1717.     if (local_wild_match(args[1], r)) {
  1718.       /* found it */
  1719.       sprintf(buff, "%d", wcount);
  1720.       return;
  1721.     }
  1722.  
  1723.     wcount++;
  1724.  
  1725.   } while (*s);
  1726.  
  1727.   strcpy(buff, "0");        /* no match */
  1728.  
  1729. }
  1730.  
  1731. FUNCTION(fun_strmatch)
  1732. {
  1733.   /* matches a wildcard pattern for an _entire_ string */
  1734.  
  1735.   if (local_wild_match(args[1], args[0]))
  1736.     strcpy(buff, "1");
  1737.   else
  1738.     strcpy(buff, "0");
  1739.  
  1740. }
  1741.  
  1742. /*  taken from the 2.0 code  */
  1743. FUNCTION(fun_wordpos)
  1744. {
  1745.   char *cp;
  1746.   char done, inspace;
  1747.   int charpos = atoi(args[1]);
  1748.   int word = 1;
  1749.  
  1750.   for (inspace = 0, done = 0, cp = args[0]; cp && *cp && !done; cp++) {
  1751.     if ((*cp == ' ') && (!inspace)) {
  1752.       word++;
  1753.       inspace = 1;
  1754.     }
  1755.     if ((*cp != ' ') && (inspace))
  1756.       inspace = 0;
  1757.     if ((cp - args[0] + 1) == charpos)
  1758.       done = 1;
  1759.   }
  1760.   if (!done)
  1761.     strcpy(buff, "#-1");
  1762.   else
  1763.     sprintf(buff, "%d", word);
  1764. }
  1765.  
  1766. FUNCTION(fun_extract)
  1767. {
  1768.   int start = atoi(args[1]), len = atoi(args[2]);
  1769.   char *s = args[0], *r;
  1770.   if ((start < 1) || (len < 1)) {
  1771.     *buff = 0;
  1772.     return;
  1773.   }
  1774.   start--;
  1775.   while (start && *s) {
  1776.     while (*s && (*s == ' '))
  1777.       s++;
  1778.     while (*s && (*s != ' '))
  1779.       s++;
  1780.     start--;
  1781.   }
  1782.   while (*s && (*s == ' '))
  1783.     s++;
  1784.   r = s;
  1785.   while (len && *s) {
  1786.     while (*s && (*s == ' '))
  1787.       s++;
  1788.     while (*s && (*s != ' '))
  1789.       s++;
  1790.     len--;
  1791.   }
  1792.   *s = 0;
  1793.   if(strlen(r) < BUFFER_LEN)
  1794.     strcpy(buff, r);
  1795.   else
  1796.     buff[0] = '\0';
  1797. }
  1798.  
  1799. int translate(arg)
  1800.     char *arg;
  1801. {
  1802.   int temp;
  1803.   char *temp2;
  1804.  
  1805.   if (arg[0] == '#')
  1806.     if ((temp = atoi(arg+1)) == -1)
  1807.       return 0;
  1808.     else
  1809.       return temp;
  1810.   else
  1811.     temp2 = arg;
  1812.     while (isspace(*temp2))
  1813.       temp2++;
  1814.     if ((*temp2 == '\0') && (temp2 == arg))
  1815.       return 0;
  1816.     if (isdigit(*temp2))
  1817.       return atoi(temp2);
  1818.     return 1;
  1819. }
  1820.  
  1821. FUNCTION(fun_cat)
  1822. {
  1823.   char *bp;
  1824.   int i;
  1825.  
  1826.   bp = buff;
  1827.   safe_str(args[0], buff, &bp);
  1828.   for (i = 1; i < nargs; i++) {
  1829.     safe_chr(' ', buff, &bp);
  1830.     safe_str(args[i], buff, &bp);
  1831.   }
  1832.   *bp = '\0';
  1833. }
  1834.  
  1835. FUNCTION(fun_strcat)
  1836. {
  1837.   char *bp;
  1838.  
  1839.   bp = buff;
  1840.   safe_str(args[0], buff, &bp);
  1841.   safe_str(args[1], buff, &bp);
  1842.   *bp = '\0';
  1843. }
  1844.  
  1845. FUNCTION(fun_remove)
  1846. {
  1847.     char *s, *t, *p;
  1848.  
  1849.     /*
  1850.      * This function is 'safe' since it is impossible that any of the arg[]
  1851.      * elements will be larger than BUFFER_LEN, and in the worst case, this
  1852.      * does nothing to the the length, so still < BUFFER_LEN
  1853.      */
  1854.  
  1855.     if (index(args[1],' ')) {
  1856.     strcpy(buff, "#-1 CAN ONLY DELETE ONE ELEMENT");
  1857.     return;
  1858.     }
  1859.  
  1860.     /* Jump to each space in the list (except for the first word).
  1861.      * Move over the word. If it doesn't match, jump to the next space
  1862.      * and try again. We have to compare until the end of the word we
  1863.      * want to match, and that should put us at the end of the word
  1864.      * in the list. Otherwise, we go seeking again. If we don't find
  1865.      * anything, we just copy the original string.
  1866.      */
  1867.  
  1868.     s = p = args[0];
  1869.  
  1870.      while (s) {
  1871.  
  1872.      t = args[1];
  1873.      if (*p != *t) {
  1874.          if ((s = (char *)index(p, ' ')) != NULL)
  1875.          p = s + 1;
  1876.          continue;
  1877.      }
  1878.      while (*p && *t && (*p == *t)) {
  1879.          p++;
  1880.          t++;
  1881.      }
  1882.      if ((*t == '\0') && ((*p == ' ') || (*p == '\0'))) {
  1883.          if (*p == '\0') {               /* remove the last element */
  1884.          *s = '\0';
  1885.          strcpy(buff, args[0]);
  1886.          } else if (s == args[0]) {           /* remove the first element */
  1887.          p++;
  1888.          strcpy(buff, p);
  1889.          } else {                       /* remove a middle element */
  1890.          *s = '\0';
  1891.          sprintf(buff, "%s%s", args[0], p);
  1892.          }
  1893.          return;
  1894.      }
  1895.      if ((s = (char *)index(p, ' ')) != NULL)
  1896.          p = s + 1;
  1897.      }
  1898.     strcpy(buff, args[0]);
  1899. }
  1900.  
  1901.  
  1902. FUNCTION(fun_member)
  1903. {
  1904.   char *s, *t;
  1905.   int el;
  1906.  
  1907.   if (index(args[1],' ')) {
  1908.     strcpy(buff,"#-1 CAN ONLY TEST ONE ELEMENT");
  1909.     return;
  1910.   }
  1911.  
  1912.   s = args[0];
  1913.   el = 1;
  1914.  
  1915.   do {
  1916.     t = skip_space(s);
  1917.     s = seek_char(t, ' ');
  1918.     if (*s)
  1919.       *s++ = '\0';
  1920.     if (!strcmp(args[1], t)) {
  1921.       sprintf(buff, "%d", el);
  1922.       return;
  1923.     }
  1924.     el++;
  1925.   } while (*s);
  1926.  
  1927.   strcpy(buff, "0");        /* not found */
  1928. }
  1929.  
  1930. FUNCTION(fun_before)
  1931. {
  1932.   int n = strlen(args[1]);
  1933.   char *s = args[0];
  1934.   char *word = args[1];
  1935.   char *p;
  1936.  
  1937.   if (!*word) {
  1938.     /* second argument is a space. Act like "first" function */
  1939.     p = (char *) index(s, ' ');
  1940.     if (p != NULL)
  1941.       *p = '\0';        /* cut off after first word */
  1942.     strcpy(buff, s);
  1943.     return;
  1944.   }
  1945.  
  1946.   while (*s) {
  1947.     /* find the first character in the string that matches the first
  1948.      * character of the target word
  1949.      */
  1950.     p = (char *) index(s, *word);
  1951.  
  1952.     if (p == NULL) {
  1953.       /* target string not found, return original string */
  1954.       strcpy(buff, args[0]);
  1955.       return;
  1956.     }
  1957.  
  1958.     /* check to see if the rest of the word matches */
  1959.     if (!strncmp(p, word, n)) {
  1960.       /* exact match. Don't need to worry about overflowing buffers,
  1961.        * so just truncate and return string.
  1962.        */
  1963.       *p = '\0';
  1964.       strcpy(buff, args[0]);
  1965.       return;
  1966.     }
  1967.  
  1968.     /* rest of the word didn't match, keep looking */
  1969.     s = p + 1;
  1970.   }
  1971.  
  1972.   /* didn't find anything, just return original string */
  1973.   strcpy(buff, args[0]);
  1974. }
  1975.  
  1976. FUNCTION(fun_after)
  1977. {
  1978.   int n = strlen(args[1]);
  1979.   char *s = args[0];
  1980.   char *word = args[1];
  1981.   char *p;
  1982.  
  1983.   if (!*word) {
  1984.     /* second argument is a space. Act like "rest" function */
  1985.     p = (char *) index(s, ' ');
  1986.     if (p == NULL)
  1987.       *buff = '\0';
  1988.     else
  1989.       strcpy(buff, p + 1);
  1990.     return;
  1991.   }
  1992.  
  1993.   while (*s) {
  1994.     /* find the first character in the string that matches the first
  1995.      * character of the target word
  1996.      */
  1997.     p = (char *) index(s, *word);
  1998.  
  1999.     if (p == NULL) {
  2000.       /* target string not found, return nothing */
  2001.       *buff = '\0';
  2002.       return;
  2003.     }
  2004.     
  2005.     /* check to see if the rest of the word matches */
  2006.     if (!strncmp(p, word, n)) {
  2007.       /* exact match, copy and return. No need to check to see if we
  2008.        * overflowed the buffer, since the string will be shorter.
  2009.        */
  2010.       s = p + n;
  2011.       strcpy(buff, s);
  2012.       return;
  2013.     }
  2014.  
  2015.     /* rest of the word didn't match, keep looking */
  2016.     s = p + 1;
  2017.   }
  2018.  
  2019.   /* didn't find anything */
  2020.   *buff = '\0';
  2021. }
  2022.  
  2023. void do_flip(s, r)
  2024.      char *s;
  2025.      char *r;
  2026.   /* utility function to reverse a string */
  2027. {
  2028.   char *p;
  2029.  
  2030.   p = strlen(s) + r;
  2031.   *p-- = '\0';
  2032.   while (*s) 
  2033.     *p-- = *s++;
  2034. }
  2035.  
  2036. FUNCTION(fun_flip)
  2037. {
  2038.   do_flip(args[0], buff);
  2039. }
  2040.  
  2041. FUNCTION(fun_revwords)
  2042. {
  2043.   /* based on the 2.0 code */
  2044.  
  2045.   char tbuf1[BUFFER_LEN];
  2046.   char *bp, *tp, *lp;
  2047.   char c;
  2048.  
  2049.   /* reverse the string */
  2050.   do_flip(args[0], tbuf1);
  2051.  
  2052.   /* reverse each word in the string. The words themselves will then be
  2053.    * forwards again, with their order reversed (from the prior flip).
  2054.    */
  2055.  
  2056.   tp = tbuf1;
  2057.   bp = buff;
  2058.  
  2059.   while (*tp) {
  2060.     lp = tp;
  2061.  
  2062.     while (!isspace(*tp))    /* skip spaces */
  2063.       tp++;
  2064.  
  2065.     if (tp != lp) {
  2066.       c = *tp;
  2067.       *tp = '\0';        /* split off a word */
  2068.       do_flip(lp, bp);        /* reverse word */
  2069.       bp += tp - lp;        /* move pointer past stuff we've added */
  2070.       *tp = c;            /* put back the original char */
  2071.     }
  2072.     if (!*tp)
  2073.       break;
  2074.     *bp++ = *tp++;
  2075.   }
  2076.   *bp = '\0';            /* terminate */
  2077. }
  2078.  
  2079. static int do_wordcount(str)
  2080.      char *str;
  2081. {
  2082.     /* count the number of words in a string */
  2083.  
  2084.     char *s = str;
  2085.     int count = 0;
  2086.  
  2087.     while (*s && (*s == ' '))
  2088.     s++;
  2089.  
  2090.     while (*s) {
  2091.     count++;
  2092.     while (*s && (*s != ' '))
  2093.         s++;
  2094.     while (*s && (*s == ' '))
  2095.         s++;
  2096.     }
  2097.  
  2098.     return (count);
  2099. }
  2100.  
  2101. FUNCTION(fun_words)
  2102. {
  2103.   sprintf(buff, "%d", do_wordcount(args[0]));
  2104. }
  2105.  
  2106. FUNCTION(fun_merge)
  2107. {
  2108.   /* given s1, s2, and a char, for each character in s1, if the char
  2109.    * is the same as the given char, replace it with the corresponding
  2110.    * char in s2.
  2111.    */
  2112.  
  2113.   char *str, *rep, *bp;
  2114.   char c;
  2115.  
  2116.   /* do length checks first */
  2117.   if (strlen(args[0]) != strlen(args[1])) {
  2118.     strcpy(buff, "#-1 STRING LENGTHS MUST BE EQUAL");
  2119.     return;
  2120.   }
  2121.   if (strlen(args[2]) > 1) {
  2122.     strcpy(buff, "#-1 TOO MANY CHARACTERS");
  2123.     return;
  2124.   }
  2125.  
  2126.   /* find the character to look for */
  2127.   if (!*args[2])
  2128.     c = ' ';
  2129.   else
  2130.     c = *args[2];
  2131.  
  2132.   /* walk strings, copy from the appropriate string */
  2133.   for (str = args[0], rep = args[1], bp = buff;
  2134.        *str && *rep;
  2135.        str++, rep++, bp++) {
  2136.     if (*str == c)
  2137.       *bp = *rep;
  2138.     else
  2139.       *bp = *str;
  2140.   }
  2141.  
  2142.   *bp = '\0';            /* terminate */
  2143.  
  2144.   /* there is no error checking necessary since everything passed in
  2145.    * will be smaller than BUFFER_LEN, and the string cannot be any
  2146.    * larger, since we're just doing a copy.
  2147.    */
  2148. }
  2149.  
  2150. FUNCTION(fun_splice)
  2151. {
  2152.   /* like MERGE(), but does it for a word */
  2153.  
  2154.   char *bp;
  2155.   char *p1, *p2;
  2156.   char *q1, *q2;
  2157.   int words;
  2158.   int i;
  2159.  
  2160.   /* length checks */
  2161.   if (!*args[2]) {
  2162.     strcpy(buff, "#-1 NEED A WORD");
  2163.     return;
  2164.   }
  2165.   if (do_wordcount(args[2]) != 1) {
  2166.     strcpy(buff, "#-1 TOO MANY WORDS");
  2167.     return;
  2168.   }
  2169.  
  2170.   words = do_wordcount(args[0]);
  2171.   if (words != do_wordcount(args[1])) {
  2172.     strcpy(buff, "#-1 NUMBER OF WORDS MUST BE EQUAL");
  2173.     return;
  2174.   }
  2175.  
  2176.   /* loop through the two lists */
  2177.   for (bp = buff, i = 0, p1 = args[0], q1 = args[1];
  2178.        i < words; 
  2179.        i++, p1 = p2, q1 = q2) {
  2180.     if ((p2 = (char *) index(p1, ' ')) != NULL)
  2181.       *p2++ = '\0';
  2182.     if ((q2 = (char *) index(q1, ' ')) != NULL)
  2183.       *q2++ = '\0';
  2184.     if (bp != buff)
  2185.       safe_chr(' ', buff, &bp);
  2186.     if (!strcmp(p1, args[2]))
  2187.       safe_str(q1, buff, &bp);         /* replace */
  2188.     else
  2189.       safe_str(p1, buff, &bp);         /* copy */
  2190.   }
  2191.   *bp = '\0';
  2192. }
  2193.  
  2194. FUNCTION(fun_lcstr)
  2195. {
  2196.   char *ap;
  2197.   ap = args[0];
  2198.   *buff = '\0';
  2199.   while (*ap) {
  2200.     if (isupper(*ap))
  2201.       *buff++ = tolower(*ap++);
  2202.     else
  2203.       *buff++ = *ap++;
  2204.   }
  2205.   *buff++ = '\0';
  2206.   /*  No need to check buffer length  */
  2207. }
  2208.  
  2209. FUNCTION(fun_ucstr)
  2210. {
  2211.   char *ap;
  2212.   ap = args[0];
  2213.   *buff = '\0';
  2214.   while (*ap) {
  2215.     if (islower(*ap))
  2216.       *buff++ = toupper(*ap++);
  2217.     else
  2218.       *buff++ = *ap++;
  2219.   }
  2220.   *buff++ = '\0';
  2221.   /*  No need to check buffer length */
  2222. }
  2223.  
  2224. FUNCTION(fun_repeat)
  2225. {
  2226.   int times, i;
  2227.   char *bp;
  2228.  
  2229.   times = atoi(args[1]);
  2230.   if (times < 1) {
  2231.     *buff = '\0';
  2232.     return;
  2233.   }
  2234.   if (times == 1) {
  2235.     strcpy(buff, args[0]);
  2236.     return;
  2237.   }
  2238.   if (strlen(args[0]) * times >= BUFFER_LEN) {
  2239.     strcpy(buff, "#-1 STRING TOO LONG");
  2240.     return;
  2241.   }
  2242.  
  2243.   bp = buff;
  2244.   for (i = 0; i < times; i++)
  2245.     safe_str(args[0], buff, &bp);
  2246.   *bp = '\0';
  2247. }
  2248.  
  2249. FUNCTION(fun_scramble)
  2250. {
  2251.   int n, i, j;
  2252.   char c;
  2253.  
  2254.   if (!args[0] || !*args[0]) {
  2255.     *buff = '\0';
  2256.     return;
  2257.   }
  2258.   strcpy(buff, args[0]);
  2259.  
  2260.   n = strlen(buff);        /* just the length of the string */
  2261.   for (i = 0; i < n; i++) {
  2262.     j = getrandom(n - i) + i;
  2263.     c = buff[i];
  2264.     buff[i] = buff[j];
  2265.     buff[j] = c;
  2266.   }
  2267. }
  2268.  
  2269. FUNCTION(fun_grep)
  2270. {
  2271.   char *sptr[10];
  2272.   int i;
  2273.   char *tp;
  2274.  
  2275.   dbref it = match_thing(privs, args[0]);
  2276.   if (it == NOTHING) {
  2277.     strcpy(buff, "#-1 NO SUCH OBJECT VISIBLE");
  2278.     return;
  2279.   }
  2280.  
  2281.   /* make sure there's an attribute and a pattern */
  2282.   if (!args[1] || !*args[1]) {
  2283.     strcpy(buff, "#-1 NO SUCH ATTRIBUTE");
  2284.     return;
  2285.   }
  2286.   if (!args[2] || !*args[2]) {
  2287.     strcpy(buff, "#-1 INVALID GREP PATTERN");
  2288.     return;
  2289.   }
  2290.  
  2291.   /* save wptr */
  2292.   for (i = 0; i < 10; i++)
  2293.     sptr[i] = wptr[i];
  2294.   
  2295.   tp = grep_util(it, args[1], args[2], strlen(args[2]));
  2296.   strcpy(buff, tp);
  2297.   free(tp);
  2298.  
  2299.   /* restore wptr */
  2300.   for (i = 0; i < 10; i++)
  2301.     wptr[i] = sptr[i];
  2302. }
  2303.  
  2304. FUNCTION(fun_ljust)
  2305. {
  2306.   /* pads a string with trailing blanks (or other fill character) */
  2307.  
  2308.   int spaces, i;
  2309.   char c;
  2310.   char *bp;
  2311.  
  2312.   if (!ok_nargs("LJUST", nargs, 2, 3, buff))
  2313.       return;
  2314.  
  2315.   spaces = atoi(args[1]) - strlen(args[0]);
  2316.   if (nargs == 3)
  2317.     c = *args[2];
  2318.   else
  2319.     c = ' ';
  2320.  
  2321.   if (spaces <= 0) {
  2322.     /* no padding needed, just return string */
  2323.     strcpy(buff, args[0]);
  2324.     return;
  2325.   }
  2326.   if (spaces > BUFFER_LEN)
  2327.     spaces = BUFFER_LEN;
  2328.  
  2329.   bp = buff;
  2330.   safe_str(args[0], buff, &bp);
  2331.   for (i = 0; i < spaces; i++)
  2332.     safe_chr(c, buff, &bp);
  2333.   *bp = '\0';
  2334. }
  2335.  
  2336. FUNCTION(fun_rjust)
  2337. {
  2338.   /* pads a string with leading blanks */
  2339.  
  2340.   int spaces, i;
  2341.   char c;
  2342.   char *bp;
  2343.  
  2344.   if (!ok_nargs("RJUST", nargs, 2, 3, buff))
  2345.       return;
  2346.  
  2347.   spaces = atoi(args[1]) - strlen(args[0]);
  2348.   if (nargs == 3)
  2349.     c = *args[2];
  2350.   else
  2351.     c = ' ';
  2352.  
  2353.   if (spaces <= 0) {
  2354.     /* no padding needed, just return string */
  2355.     strcpy(buff, args[0]);
  2356.     return;
  2357.   }
  2358.   if (spaces > BUFFER_LEN)
  2359.     spaces = BUFFER_LEN;
  2360.  
  2361.   bp = buff;
  2362.   for (i = 0; i < spaces; i++)
  2363.     safe_chr(c, buff, &bp);
  2364.   safe_str(args[0], buff, &bp);
  2365.   *bp = '\0';
  2366. }
  2367.   
  2368. /* --------------------------------------------------------------------------
  2369.  * Functions involving lists with arbitrary separators:  ITEMS, ELEMENT,
  2370.  *    INDEX, INSERT, REPLACE, DELETE
  2371.  */
  2372.  
  2373. FUNCTION(fun_items)
  2374. {
  2375.     /* the equivalent of WORDS for an arbitrary separator */
  2376.  
  2377.     char *s = args[0];
  2378.     char c = *args[1];
  2379.     int count = 0;
  2380.  
  2381.     if (c == '\0')
  2382.     c = ' ';
  2383.  
  2384.     while (*s) {
  2385.     count++;
  2386.     do {
  2387.         s++;
  2388.     } while (*s && (*s != c));
  2389.     }
  2390.  
  2391.     sprintf(buff, "%d", count);
  2392. }
  2393.  
  2394. FUNCTION(fun_element)
  2395. {
  2396.     /* the equivalent of MEMBER for an arbitrary separator */
  2397.  
  2398.     char *s, *t;
  2399.     char c;
  2400.     int el;
  2401.  
  2402.     c = *args[2];
  2403.  
  2404.     if (c != '\0') {
  2405.     if (index(args[1], c)) {
  2406.         strcpy(buff, "#-1 CAN ONLY TEST ONE ELEMENT");
  2407.         return;
  2408.     }
  2409.     } else {
  2410.     c = ' ';
  2411.     }
  2412.  
  2413.     s = args[0];
  2414.     el = 1;
  2415.  
  2416.     do {
  2417.     t = s;
  2418.     s = seek_char(t, c);
  2419.     if (*s)
  2420.         *s++ = '\0';
  2421.     if (local_wild_match(args[1], t)) {
  2422.         sprintf(buff, "%d", el);
  2423.         return;
  2424.     }
  2425.     el++;
  2426.     } while (*s);
  2427.  
  2428.     strcpy(buff, "0");        /* no match */
  2429. }
  2430.  
  2431. FUNCTION(fun_index)
  2432. {
  2433.   /* more or less the equivalent of EXTRACT for an arbitrary separator */
  2434.  
  2435.   int start, end;
  2436.   char c;
  2437.   char *s, *p, *bp;
  2438.  
  2439.   s = args[0];
  2440.   c = *args[1];
  2441.   if (!c)
  2442.       c = ' ';
  2443.  
  2444.   start = atoi(args[2]);
  2445.   end = atoi(args[3]);
  2446.  
  2447.   *buff = '\0';
  2448.   bp = buff;
  2449.  
  2450.   if ((start < 1) || (end < 1) || (*s == '\0'))
  2451.     return;
  2452.  
  2453.   /* move s to the start of the item we want */
  2454.   start--;
  2455.   while (start && s && *s) {
  2456.     if ((s = (char *) index(s, c)) != NULL)   s++;
  2457.     start--;
  2458.   }
  2459.  
  2460.  /* skip just spaces, not tabs or newlines, since people may MUSHcode things
  2461.   * like "%r%tPolgara %r%tDurnik %r%tJavelin"
  2462.   */
  2463.   while (s && *s && (*s == ' '))   s++;
  2464.   if (!s || !*s)   return;
  2465.  
  2466.   /* now figure out where to end the string */
  2467.   p = s;
  2468.   while (end && p && *p) {
  2469.     if ((p = (char *) index(p, c)) != NULL) {
  2470.       if (--end == 0) {
  2471.     /* trim trailing spaces (just true spaces) */
  2472.     do {
  2473.       p--;
  2474.     } while (*p == ' ');
  2475.     *(++p) = '\0';
  2476.     safe_str(s, buff, &bp);
  2477.     *bp = '\0';
  2478.     return;
  2479.       } else
  2480.     p++;
  2481.     }
  2482.   }
  2483.  
  2484.   /* if we've hit this point, we've run off the end of the string */
  2485.   safe_str(s, buff, &bp);
  2486.   *bp = '\0';
  2487. }
  2488.  
  2489. static void do_itemfuns(buff, str, num, word, sep, flag)
  2490.      char *buff;        /* the return buffer */
  2491.      char *str;            /* the original string */
  2492.      char *num;            /* the element number */
  2493.      char *word;        /* word to insert or replace */
  2494.      char *sep;            /* the separator */
  2495.      int flag;            /* op -- 0 delete, 1 replace, 2 insert */
  2496. {
  2497.     char c;
  2498.     int el, count;
  2499.     char *sptr, *eptr, *bp;
  2500.  
  2501.     /* figure out the separator character */
  2502.     if (sep && *sep)
  2503.     c = *sep;
  2504.     else 
  2505.     c = ' ';
  2506.  
  2507.     el = atoi(num);
  2508.  
  2509.     if (el < 1) {        /* no such position */
  2510.     strcpy(buff, str);
  2511.     return;
  2512.     }
  2513.  
  2514.     sptr = str;
  2515.     count = 1;
  2516.  
  2517.     /* we can't remove anything before the first position */
  2518.     if (el < 1) {
  2519.     strcpy(buff, str);
  2520.     return;
  2521.     }
  2522.  
  2523.     /* go to the correct item in the string */
  2524.     while (sptr && (count < el)) {
  2525.     sptr++;
  2526.     sptr = (char *)index(sptr, c);
  2527.     count++;
  2528.     }
  2529.  
  2530.     if (!sptr) {
  2531.     /* we've run off the end of the string without finding anything */
  2532.     strcpy(buff, str);
  2533.     return;
  2534.     }
  2535.  
  2536.     /* now find the end of that element */
  2537.     if (sptr != str)
  2538.     *sptr++ = '\0';
  2539.     eptr = (char *)index(sptr, c);
  2540.  
  2541.     switch (flag) {
  2542.       case 0:
  2543.     /* deletion */
  2544.     if (!eptr) {     /* last element in the string */
  2545.         strcpy(buff, str);
  2546.     } else if (sptr == str) {          /* first element in the string */
  2547.         *eptr++ = '\0';    /* chop leading separator */
  2548.         strcpy(buff, eptr);
  2549.     } else {
  2550.         sprintf(buff, "%s%s", str, eptr);
  2551.     }
  2552.     break;
  2553.       case 1:
  2554.     /* replacing */
  2555.     bp = buff;
  2556.     if (!eptr) {        /* last element in string */
  2557.         safe_str(str, buff, &bp);
  2558.         safe_chr(c, buff, &bp);
  2559.         safe_str(word, buff, &bp);
  2560.     } else if (sptr == str) {     /* first element in string */
  2561.         safe_str(word, buff, &bp);
  2562.         safe_str(eptr, buff, &bp);
  2563.     } else {
  2564.         safe_str(str, buff, &bp);
  2565.         safe_chr(c, buff, &bp);
  2566.         safe_str(word, buff, &bp);
  2567.         safe_str(eptr, buff, &bp);
  2568.     }
  2569.     *bp = '\0';
  2570.     break;
  2571.       case 2:
  2572.     /* insertion */
  2573.     bp = buff;
  2574.     if (sptr == str) {    /* first element in string */
  2575.         safe_str(word, buff, &bp);
  2576.         safe_chr(c, buff, &bp);
  2577.         safe_str(str, buff, &bp);
  2578.     } else {
  2579.         safe_str(str, buff, &bp);
  2580.         safe_chr(c, buff, &bp);
  2581.         safe_str(word, buff, &bp);
  2582.         safe_chr(c, buff, &bp);
  2583.         safe_str(sptr, buff, &bp);
  2584.     }
  2585.     *bp = '\0';
  2586.     break;
  2587.     }
  2588. }
  2589.  
  2590.  
  2591. FUNCTION(fun_delete)
  2592. {
  2593.     /* delete a word at position X of a list */
  2594.  
  2595.     if (!ok_nargs("DELETE", nargs, 2, 3, buff))
  2596.     return;
  2597.  
  2598.     do_itemfuns(buff, args[0], args[1], NULL, args[2], 0);
  2599. }
  2600.  
  2601. FUNCTION(fun_replace)
  2602. {
  2603.     /* replace a word at position X of a list */
  2604.  
  2605.     if (!ok_nargs("REPLACE", nargs, 3, 4, buff))
  2606.     return;
  2607.     
  2608.     do_itemfuns(buff, args[0], args[1], args[2], args[3], 1);
  2609. }
  2610.  
  2611. FUNCTION(fun_insert)
  2612. {
  2613.     /* insert a word at position X of a list */
  2614.  
  2615.     if (!ok_nargs("INSERT", nargs, 3, 4, buff))
  2616.     return;
  2617.     
  2618.     do_itemfuns(buff, args[0], args[1], args[2], args[3], 2);
  2619. }
  2620.  
  2621. /* --------------------------------------------------------------------------
  2622.  * Word functions: CAPSTR, ART, SUBJ, OBJ, POSS, ALPHAMAX, ALPHAMIN, ISWORD
  2623.  */
  2624.  
  2625. FUNCTION(fun_isword)
  2626. {
  2627.   /* is every character a letter? */
  2628.  
  2629.   char *p;
  2630.   for (p = args[0]; *p; p++) {
  2631.     if (!isalpha(*p)) {
  2632.       strcpy(buff, "0");
  2633.       return;
  2634.     }
  2635.   }
  2636.   strcpy(buff, "1");
  2637. }
  2638.  
  2639. FUNCTION(fun_capstr)
  2640. {
  2641.   strcpy(buff, args[0]);
  2642.   if (islower(*buff))
  2643.     *buff = toupper(*buff);
  2644.   /*  No need to check buffer length  */
  2645. }
  2646.  
  2647. FUNCTION(fun_art)
  2648. {
  2649.   /* checks a word and returns the appropriate article, "a" or "an" */
  2650.  
  2651.   char c = tolower(*args[0]);
  2652.   if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u')
  2653.     strcpy(buff, "an");
  2654.   else
  2655.     strcpy(buff, "a");
  2656. }
  2657.  
  2658. /* utility function to find sex of object, used by SUBJ, OBJ, and POSS.
  2659.  * Returns 0 for none, 1 for female, 2 for male 
  2660.  */
  2661. int find_sex(thing)
  2662.      dbref thing;
  2663. {
  2664.   ATTR *a;
  2665.   int gender;
  2666.  
  2667.   a = atr_get(thing, "SEX");
  2668.   if (!a)
  2669.     gender = 0;
  2670.   else {
  2671.     switch (*uncompress(a->value)) {
  2672.     case 'M': case 'm':
  2673.       gender = 2;
  2674.       break;
  2675.     case 'W': case 'w': case 'F': case 'f':
  2676.       gender = 1;
  2677.       break;
  2678.     default:
  2679.       gender = 0;
  2680.     }
  2681.   }
  2682.   return gender;
  2683. }
  2684.  
  2685. FUNCTION(fun_subj)
  2686. {
  2687.   dbref thing;
  2688.   int gender;
  2689.  
  2690.   thing = match_thing(privs, args[0]);
  2691.  
  2692.   if (thing == NOTHING) {
  2693.     strcpy(buff, "#-1 NO MATCH");
  2694.     return;
  2695.   }
  2696.  
  2697.   gender = find_sex(thing);
  2698.   switch (gender) {
  2699.   case 1:
  2700.     strcpy(buff, "she");
  2701.     break;
  2702.   case 2:
  2703.     strcpy(buff, "he");
  2704.     break;
  2705.   default:
  2706.     strcpy(buff, "it");
  2707.   }
  2708. }
  2709.  
  2710. FUNCTION(fun_obj)
  2711. {
  2712.   dbref thing;
  2713.   int gender;
  2714.  
  2715.   thing = match_thing(privs, args[0]);
  2716.  
  2717.   if (thing == NOTHING) {
  2718.     strcpy(buff, "#-1 NO MATCH");
  2719.     return;
  2720.   }
  2721.  
  2722.   gender = find_sex(thing);
  2723.   switch (gender) {
  2724.   case 1:
  2725.     strcpy(buff, "her");
  2726.     break;
  2727.   case 2:
  2728.     strcpy(buff, "him");
  2729.     break;
  2730.   default:
  2731.     strcpy(buff, "it");
  2732.   }
  2733. }
  2734.  
  2735. FUNCTION(fun_poss)
  2736. {
  2737.   dbref thing;
  2738.   int gender;
  2739.  
  2740.   thing = match_thing(privs, args[0]);
  2741.  
  2742.   if (thing == NOTHING) {
  2743.     strcpy(buff, "#-1 NO MATCH");
  2744.     return;
  2745.   }
  2746.  
  2747.   gender = find_sex(thing);
  2748.   switch (gender) {
  2749.   case 1:
  2750.     strcpy(buff, "her");
  2751.     break;
  2752.   case 2:
  2753.     strcpy(buff, "his");
  2754.     break;
  2755.   default:
  2756.     strcpy(buff, "its");
  2757.   }
  2758. }
  2759.  
  2760. FUNCTION(fun_alphamax)
  2761. {
  2762.   char *amax;
  2763.   int i = 1;
  2764.   if (!args[0]) {
  2765.     strcpy(buff, "#-1 TOO FEW ARGUMENTS");
  2766.     return;
  2767.   } else
  2768.     amax = args[0];
  2769.   while ((i < 10) && args[i]) {
  2770.     amax = (strcmp(amax, args[i]) > 0) ? amax : args[i];
  2771.     i++;
  2772.   }
  2773.   sprintf(buff, "%s", amax);
  2774. }
  2775.  
  2776. FUNCTION(fun_alphamin)
  2777. {
  2778.   char *amin;
  2779.   int i = 1;
  2780.   if (!args[0]) {
  2781.     strcpy(buff, "#-1 TOO FEW ARGUMENTS");
  2782.     return;
  2783.   } else
  2784.     amin = args[0];
  2785.   while ((i < 10) && args[i]) {
  2786.     amin = (strcmp(amin, args[i]) < 0) ? amin : args[i];
  2787.     i++;
  2788.   }
  2789.   sprintf(buff, "%s", amin);
  2790. }
  2791.  
  2792.  
  2793. /* --------------------------------------------------------------------------
  2794.  * MUSH utilities: FLAGS, NUM, RNUM, LEXITS, EXITS, LCON, CON, NEXT, MAIL,
  2795.  *   NEARBY, TYPE, HASFLAG, LOCK, ELOCK, LOC, HOME, OWNER, NAME, PMATCH
  2796.  *   LOCATE, ROOM, WHERE, CONTROLS, VISIBLE
  2797.  */
  2798.  
  2799. FUNCTION(fun_flags)
  2800. {
  2801.   dbref thing;
  2802.   thing = match_thing(privs, args[0]);
  2803.  
  2804.   if (thing == NOTHING) {
  2805.     strcpy(buff,"#-1");
  2806.     return;
  2807.   }
  2808.   strcpy(buff, unparse_flags(thing, privs));
  2809. }
  2810.  
  2811. FUNCTION(fun_num)
  2812. {
  2813.   sprintf(buff, "#%d", match_thing(privs, args[0]));
  2814. }
  2815.  
  2816. #ifdef DO_GLOBALS
  2817. FUNCTION(fun_rnum)
  2818. {
  2819.   dbref place = match_thing(privs, args[0]);
  2820.   char *name = args[1];
  2821.   dbref thing;
  2822.   init_match_remote(place, name, NOTYPE);
  2823.   match_remote();
  2824.   switch (thing = match_result()) {
  2825.   case NOTHING:
  2826.     strcpy(buff, "#-1 NO MATCH");
  2827.     break;
  2828.   case AMBIGUOUS:
  2829.     strcpy(buff, "#-1 AMBIGUOUS MATCH");
  2830.     break;
  2831.   default:
  2832.     sprintf(buff, "#%d", thing);
  2833.   }
  2834. }
  2835. #endif
  2836.  
  2837. /*
  2838.  * fun_lcon, fun_lexits, fun_con, fun_exit, fun_next, and next_exit were all
  2839.  * re-coded by d'mike, 7/12/91.  next_con was added at this time as well.
  2840.  *
  2841.  * The function behavior was changed by Amberyl, to remove what she saw
  2842.  * as a security problem, since mortals could get the contents of rooms
  2843.  * they didn't control, thus (if they were willing to go through the trouble)
  2844.  * they could build a scanner to locate anything they wanted.
  2845.  *
  2846.  * You can get the contents of any room you control, regardless of whether
  2847.  * or not the object is dark. You can get the contents of your current
  2848.  * location, _except_ for dark objects (and DARK/OPAQUE rooms). You CANNOT 
  2849.  * get the contents of anything else, regardless of whether or not you have 
  2850.  * objects in it. This latter behavior is exhibited by 2.0.
  2851.  *
  2852.  * The same behavior is true for exits, except OPAQUE doesn't apply.
  2853.  */
  2854.  
  2855. FUNCTION(fun_lcon)
  2856. {
  2857.   dbref it = match_thing(privs, args[0]);
  2858.   dbref thing;
  2859.   char *bp;
  2860.   int sees_loc;
  2861.  
  2862.   bp = buff;
  2863.  
  2864.   if (it != NOTHING) {
  2865.     sees_loc = Can_Examine(privs, it) ? 1 : 0;
  2866.     if (sees_loc ||
  2867.     ((Location(privs) == it) && !Dark(it) && !Opaque(it))) {
  2868.       DOLIST(thing, Contents(it)) {
  2869.     if (!Dark(thing) || sees_loc || controls(privs, thing)) {
  2870.       if (bp != buff)
  2871.         safe_chr(' ', buff, &bp);
  2872.       safe_str(tprintf("#%d", thing), buff, &bp);
  2873.     }
  2874.       }
  2875.     }
  2876.     *bp = '\0';
  2877.   } else
  2878.     strcpy(buff, "#-1");
  2879. }
  2880.  
  2881. /* fun_con is a wrapper for next_con now. */
  2882. FUNCTION(fun_con)
  2883. {
  2884.   dbref it = match_thing(privs, args[0]);
  2885.  
  2886.   if (it != NOTHING)
  2887.     sprintf(buff, "#%d", next_con(privs, Contents(it)));
  2888.   else
  2889.     strcpy(buff, "#-1");
  2890. }
  2891.  
  2892. /* return next contents that is ok to see */
  2893. dbref next_con(player, this)
  2894.   dbref player;
  2895.   dbref this;
  2896. {
  2897.   dbref loc;
  2898.   int sees_loc;
  2899.  
  2900.   if ((this == NOTHING) || ((loc = Location(this)) == NOTHING))
  2901.     return NOTHING;
  2902.   sees_loc = Can_Examine(player, loc) ? 1 : 0;
  2903.  
  2904.   if (sees_loc || ((Location(player) == loc) && !Dark(loc) && !Opaque(loc))) {
  2905.     while ((this != NOTHING) && !sees_loc && !controls(player, this)
  2906.        && Dark(this))
  2907.       this = Next(this);
  2908.   }
  2909.   return(this);
  2910. }
  2911.  
  2912. /* return next exit that is ok to see */
  2913. dbref next_exit(player, this)
  2914.     dbref player;
  2915.     dbref this;
  2916. {
  2917.   dbref loc;
  2918.   int sees_loc;
  2919.  
  2920.   if ((this == NOTHING) || ((loc = Home(this)) == NOTHING))
  2921.     return NOTHING;
  2922.   sees_loc = Can_Examine(player, loc) ? 1 : 0;
  2923.  
  2924.   if (sees_loc || ((Location(player) == loc) && !Dark(loc))) {
  2925.     while ((this != NOTHING) && !sees_loc && !controls(player, this)
  2926.        && Dark(this))
  2927.     this = Next(this);
  2928.   }
  2929.   return(this); 
  2930. }
  2931.  
  2932. FUNCTION(fun_lexits)
  2933. {
  2934.   dbref it = match_thing(privs, args[0]);
  2935.   dbref thing;
  2936.   char *bp;
  2937.   int sees_loc;
  2938.  
  2939.   bp = buff;
  2940.  
  2941.   sees_loc = Can_Examine(privs, it) ? 1 : 0;
  2942.  
  2943.   if (it != NOTHING) { 
  2944.     if (sees_loc || ((Location(privs) == it) && !Dark(it))) {
  2945.       DOLIST(thing, Exits(it)) {
  2946.     if (!Dark(thing) || sees_loc || controls(privs, thing)) {
  2947.       if (bp != buff)
  2948.         safe_chr(' ', buff, &bp);
  2949.       safe_str(tprintf("#%d", thing), buff, &bp);
  2950.     }
  2951.       }
  2952.     }
  2953.     *bp = '\0';
  2954.   } else
  2955.     strcpy(buff,"#-1");
  2956. }
  2957.  
  2958. /* fun_exit is really just a wrapper for next_exit now... */
  2959. FUNCTION(fun_exit)
  2960. {
  2961.   dbref it = match_thing(privs, args[0]);
  2962.   if (it != NOTHING)
  2963.     sprintf(buff, "#%d", next_exit(privs, Exits(it)));
  2964.   else 
  2965.     strcpy(buff, "#-1");
  2966.   return;
  2967. }
  2968.  
  2969. FUNCTION(fun_next)
  2970. {
  2971.   dbref it = match_thing(privs, args[0]);
  2972.  
  2973.   if (it != NOTHING) {
  2974.     if (Typeof(it) != TYPE_EXIT) {
  2975.       sprintf(buff, "#%d", next_con(privs, Next(it)));
  2976.       return;
  2977.     } else {
  2978.       sprintf(buff, "#%d", next_exit(privs, Next(it)));
  2979.       return;
  2980.     }
  2981.   }
  2982.   strcpy(buff, "#-1");
  2983.   return;
  2984. }
  2985.  
  2986. FUNCTION(fun_nearby)
  2987. {
  2988.   dbref obj1 = match_thing(privs, args[0]);
  2989.   dbref obj2 = match_thing(privs, args[1]);
  2990.  
  2991.   if (!controls(privs, obj1) && !controls(privs, obj2)
  2992.       && !nearby(privs, obj1) && !nearby(privs, obj2)) {
  2993.     strcpy(buff, "#-1 NO OBJECTS CONTROLLED");
  2994.     return;
  2995.   }
  2996.  
  2997.   if (!GoodObject(obj1) || !GoodObject(obj2)) {
  2998.     strcpy(buff, "#-1");
  2999.     return;
  3000.   } else
  3001.     sprintf(buff, "%d", nearby(obj1, obj2));
  3002. }
  3003.  
  3004. FUNCTION(fun_controls)
  3005. {
  3006.   dbref it = match_thing(privs, args[0]);
  3007.   dbref thing = match_thing(privs, args[1]);
  3008.  
  3009.   sprintf(buff, "%d", controls(it, thing));
  3010. }
  3011.  
  3012. FUNCTION(fun_visible)
  3013. {
  3014.     /* check to see if we have an object-attribute pair. If we don't,
  3015.      * then we want to know about the whole object; otherwise, we're
  3016.      * just interested in a single attribute.
  3017.      * If we encounter an error, we return 0 rather than an error
  3018.      * code, since if it doesn't exist, it obviously can't see 
  3019.      * anything or be seen.
  3020.      */
  3021.  
  3022.     dbref it, thing;
  3023.     char *name;
  3024.     ATTR *a;
  3025.  
  3026.     it = match_thing(privs, args[0]);
  3027.     if (it == NOTHING) {
  3028.     strcpy(buff, "0");
  3029.     return;
  3030.     }
  3031.  
  3032.     if ((name = (char *) index(args[1], '/')) != NULL)
  3033.     *name++ = '\0';
  3034.  
  3035.     thing = match_thing(privs, args[1]);
  3036.  
  3037.     if (name) {
  3038.     a = atr_get(thing, upcasestr(name));
  3039.     sprintf(buff, "%d", Can_Read_Attr(it, thing, a));
  3040.     } else {
  3041.     sprintf(buff, "%d", Can_Examine(it, thing));
  3042.     }
  3043. }
  3044.  
  3045. FUNCTION(fun_type)
  3046. {
  3047.   dbref it = match_thing(privs, args[0]);
  3048.   if (it == NOTHING) {
  3049.     strcpy(buff, "#-1");
  3050.     return;
  3051.   }
  3052.   switch (Typeof(it)) {
  3053.   case TYPE_PLAYER:
  3054.     strcpy(buff, "PLAYER");
  3055.     break;
  3056.   case TYPE_THING:
  3057.     strcpy(buff, "THING");
  3058.     break;
  3059.   case TYPE_EXIT:
  3060.     strcpy(buff, "EXIT");
  3061.     break;
  3062.   case TYPE_ROOM:
  3063.     strcpy(buff, "ROOM");
  3064.     break;
  3065.   default:
  3066.     strcpy(buff, "WEIRD OBJECT");
  3067.     fprintf(stderr, "WARNING: Weird object #%d (type %d)\n", it, Typeof(it));
  3068.   }
  3069. }
  3070.  
  3071. FUNCTION(fun_hasflag)
  3072. {
  3073.   int f = 0;
  3074.   int toggle;
  3075.   dbref it = match_thing(privs, args[0]);
  3076.   char *p = args[1];
  3077.   if (it == NOTHING) {
  3078.     strcpy(buff, "#-1");
  3079.     return;
  3080.   }
  3081.   f = find_flag(p, Typeof(it), &toggle, 0);
  3082.   if (f == -1)
  3083.     strcpy(buff, "0");
  3084.   else if (toggle == 0)
  3085.     strcpy(buff, (Flags(it) & f) ? "1" : "0");
  3086.   else
  3087.     strcpy(buff, ((Toggles(it) & f) && 
  3088.           ((f != PLAYER_SUSPECT) || See_All(privs))) ? "1" : "0");
  3089. }
  3090.  
  3091. static int handle_flaglists(player, name, fstr, type)
  3092.      dbref player;
  3093.      char *name;
  3094.      char *fstr;
  3095.      int type;            /* 0 for orflags, 1 for andflags */
  3096. {
  3097.     char *s;
  3098.     object_flag_type flag;
  3099.     int toggle, negate, temp;
  3100.     int ret = type;
  3101.     dbref it = match_thing(player, name);
  3102.  
  3103.     toggle = negate = temp = 0;
  3104.  
  3105.     if (it == NOTHING)
  3106.     return 0;
  3107.  
  3108.     for (s = fstr; *s; s++) {
  3109.  
  3110.     /* Check for a negation sign. If we find it, we note it and 
  3111.      * increment the pointer to the next character.
  3112.      */
  3113.  
  3114.     if (*s == '!') {
  3115.         negate = 1;
  3116.         s++;
  3117.     }
  3118.  
  3119.     if (!*s || ((flag = letter_to_flag(*s, Typeof(it), &toggle)) == -1)) {
  3120.  
  3121.         /* Either we got a '!' that wasn't followed by a letter, or
  3122.          * we couldn't find that flag. For AND, since we've failed
  3123.          * a check, we can return false. Otherwise we just go on.
  3124.          */
  3125.  
  3126.         if (type == 1)
  3127.         return 0;
  3128.         else
  3129.         continue;
  3130.  
  3131.     } else {
  3132.  
  3133.         /* does the object have this flag? */
  3134.  
  3135.         if ((!toggle && (Flags(it) & flag)) ||
  3136.         (toggle && (Toggles(it) & flag)))
  3137.         temp = 1;
  3138.         else
  3139.         temp = 0;
  3140.  
  3141.         if ((type == 1) && ((negate && temp) || (!negate && !temp))) {
  3142.  
  3143.         /* Too bad there's no NXOR function...
  3144.          * At this point we've either got a flag and we don't want
  3145.          * it, or we don't have a flag and we want it. Since it's
  3146.          * AND, we return false.
  3147.          */
  3148.         return 0;
  3149.  
  3150.         } else if ((type == 0) &&
  3151.                ((!negate && temp) || (negate && !temp))) {
  3152.  
  3153.         /* We've found something we want, in an OR. We OR a
  3154.          * true with the current value.
  3155.          */
  3156.  
  3157.         ret |= 1;
  3158.         }
  3159.         /* Otherwise, we don't need to do anything. */
  3160.     }
  3161.     }
  3162.     return (ret);
  3163. }
  3164.  
  3165. FUNCTION(fun_orflags)
  3166. {
  3167.     sprintf(buff, "%d", handle_flaglists(privs, args[0], args[1], 0));
  3168. }   
  3169.  
  3170. FUNCTION(fun_andflags)
  3171. {
  3172.     sprintf(buff, "%d", handle_flaglists(privs, args[0], args[1], 1));
  3173. }
  3174.  
  3175. static int get_locktype(obj, str)
  3176.      dbref obj;
  3177.      const char *str;
  3178. {
  3179.   /* figure out a lock type */
  3180.  
  3181.   if (!str || !*str || string_prefix("basic", str))
  3182.     return BASICLOCK;
  3183.  
  3184.   /* handle special cases */
  3185.   switch (Typeof(obj)) {
  3186.   case TYPE_ROOM:
  3187.     if (string_prefix("tport", str))
  3188.       return ENTERLOCK;
  3189.     break;
  3190.   case TYPE_PLAYER:
  3191.     if (string_prefix("page", str))
  3192.       return USELOCK;
  3193.     break;
  3194.   }
  3195.  
  3196.   if (string_prefix("enter", str))
  3197.     return ENTERLOCK;
  3198.   else if (string_prefix("use", str))
  3199.     return USELOCK;
  3200.  
  3201.   return NOTHING;        /* error */
  3202. }
  3203.  
  3204. FUNCTION(fun_lock)
  3205. {
  3206.   dbref it;
  3207.   const char *s;
  3208.   char *p;
  3209.   int ltype;
  3210.   
  3211.   p = (char *) index(args[0], '/');
  3212.   if (p)
  3213.     *p++ = '\0';
  3214.  
  3215.   it = match_thing(privs, args[0]);
  3216.   ltype = get_locktype(it, p);
  3217.  
  3218.   if ((it != NOTHING) && Can_Examine(privs, it) && (ltype != NOTHING)) {
  3219.     switch (ltype) {
  3220.     case BASICLOCK:
  3221.       s = unparse_boolexp(privs, Key(it), 1);
  3222.       break;
  3223.     case ENTERLOCK:
  3224.       s = unparse_boolexp(privs, Enterkey(it), 1);
  3225.       break;
  3226.     case USELOCK:
  3227.       s = unparse_boolexp(privs, Usekey(it), 1);
  3228.       break;
  3229.     }
  3230.     if (strlen(s) < BUFFER_LEN) {
  3231.       sprintf(buff, "%s", s);
  3232.       return;
  3233.     }
  3234.   }
  3235.   strcpy(buff, "#-1");
  3236.   return;
  3237. }
  3238.  
  3239. FUNCTION(fun_elock)
  3240. {
  3241.   char *p;
  3242.   int ltype;
  3243.   dbref it;
  3244.   dbref victim = match_thing(privs, args[1]);
  3245.  
  3246.   p = (char *) index(args[0], '/');
  3247.   if (p)
  3248.     *p++ = '\0';
  3249.  
  3250.   it = match_thing(privs, args[0]);
  3251.   ltype = get_locktype(it, p);
  3252.  
  3253.   if ((it != NOTHING) && (ltype != NOTHING)) {
  3254.     /*
  3255.      * We do not check for control because this can be bypassed by using
  3256.      * an indirect lock.
  3257.      */
  3258.     switch (ltype) {
  3259.     case BASICLOCK:
  3260.       sprintf(buff, "%d", eval_boolexp(victim, Key(it), it, 0, ltype));
  3261.       break;
  3262.     case ENTERLOCK:
  3263.       sprintf(buff, "%d", eval_boolexp(victim, Enterkey(it), it, 0, ltype));
  3264.       break;
  3265.     case USELOCK:
  3266.       sprintf(buff, "%d", eval_boolexp(victim, Usekey(it), it, 0, ltype));
  3267.     }
  3268.     return;
  3269.   }
  3270.  
  3271.   /* error */
  3272.   strcpy(buff, "#-1");
  3273.   return;
  3274.  
  3275.   /* No length checking in this function necessary */
  3276. }
  3277.  
  3278. FUNCTION(fun_loc)
  3279. {
  3280.   dbref it = match_thing(privs, args[0]);
  3281.   if ((it != NOTHING) && Can_Locate(privs, it)) {
  3282.     sprintf(buff, "#%d", Location(it));
  3283.     return;
  3284.   }
  3285.   strcpy(buff, "#-1");
  3286.   return;
  3287. }
  3288.  
  3289. FUNCTION(fun_where)
  3290. {
  3291.   /* finds the "real" location of an object */
  3292.  
  3293.   dbref it = match_thing(privs, args[0]);
  3294.   if ((it != NOTHING) && Can_Locate(privs, it)) {
  3295.     sprintf(buff, "#%d", where_is(it));
  3296.     return;
  3297.   }
  3298.   strcpy(buff, "#-1");
  3299.   return;
  3300. }
  3301.  
  3302. FUNCTION(fun_room)
  3303. {
  3304.   dbref room;
  3305.   int rec = 0;
  3306.   dbref it = match_thing(privs, args[0]);
  3307.  
  3308.   if ((it != NOTHING) && Can_Locate(privs, it)) {
  3309.     room = Location(it);
  3310.     if (!GoodObject(room)) {
  3311.       strcpy(buff, "#-1");
  3312.       return;
  3313.     }
  3314.     while ((Typeof(room) != TYPE_ROOM) && (rec < 15)) {
  3315.       room = Location(room);
  3316.       rec++;
  3317.     }
  3318.     if (rec > 15) {
  3319.       notify(privs, "Too many containers.");
  3320.       strcpy(buff, "#-1");
  3321.       return;
  3322.     } else {
  3323.       sprintf(buff, "#%d", room);
  3324.     }
  3325.   } else {
  3326.     notify(privs, "Permission denied.");
  3327.     strcpy(buff, "#-1");
  3328.   }
  3329. }
  3330.  
  3331. FUNCTION(fun_zone)
  3332. {
  3333.   dbref it = match_thing(privs, args[0]);
  3334.   if (it == NOTHING || !Can_Examine(privs, it)) {
  3335.     strcpy(buff, "#-1");
  3336.     return;
  3337.   }
  3338.   sprintf(buff, "#%d", Zone(it));
  3339. }
  3340.  
  3341. FUNCTION(fun_parent)
  3342. {
  3343.   dbref it = match_thing(privs, args[0]);
  3344.   if ((it == NOTHING) || !Can_Examine(privs, it)) {
  3345.     strcpy(buff, "#-1");
  3346.     return;
  3347.   }
  3348.   sprintf(buff, "#%d", Parent(it));
  3349. }
  3350.  
  3351. FUNCTION(fun_home)
  3352. {
  3353.   dbref it = match_thing(privs, args[0]);
  3354.   if ((it == NOTHING) || !Can_Examine(privs, it) ||
  3355.      (Typeof(it) == TYPE_ROOM)) {
  3356.     strcpy(buff,"#-1");
  3357.     return;
  3358.   }
  3359.   sprintf(buff, "#%d", Exits(it));
  3360. }
  3361.  
  3362. FUNCTION(fun_money)
  3363. {
  3364.   dbref it = match_thing(privs, args[0]);
  3365.   if (it == NOTHING) {
  3366.     strcpy(buff,"#-1");
  3367.     return;
  3368.   }
  3369.   sprintf(buff,"%d", Pennies(it));
  3370. }
  3371.  
  3372. FUNCTION(fun_owner)
  3373. {
  3374.   dbref it = match_thing(privs, args[0]);
  3375.   if (it != NOTHING)
  3376.     it = Owner(it);
  3377.   sprintf(buff, "#%d", it);
  3378. }
  3379.  
  3380. FUNCTION(fun_name)
  3381. {
  3382.   dbref it = match_thing(privs, args[0]);
  3383.   if (it == NOTHING)
  3384.     *buff = '\0';
  3385.   else if (Typeof(it) == TYPE_EXIT) {
  3386.     char *s;
  3387.     if(strlen(Name(it)) < BUFFER_LEN) {
  3388.       strcpy(buff, Name(it));
  3389.       for (s = buff; *s && (*s != ';'); s++) ;
  3390.       *s = 0;
  3391.     } else
  3392.       buff[0] = '\0';
  3393.   } else {
  3394.     if(strlen(Name(it)) < BUFFER_LEN)
  3395.       strcpy(buff, Name(it));
  3396.     else
  3397.       buff[0] = '\0';
  3398.   }
  3399. }
  3400.  
  3401. FUNCTION(fun_fullname)
  3402. {
  3403.     dbref it;
  3404.  
  3405.     if ((it = match_thing(privs, args[0])) == NOTHING)
  3406.     *buff = '\0';
  3407.     else
  3408.     strcpy(buff, Name(it));
  3409. }
  3410.  
  3411.  
  3412. FUNCTION(fun_pmatch)
  3413. {
  3414.   dbref target = lookup_player(args[0]);
  3415.   if (target == NOTHING)
  3416.     target = short_page(args[0]);
  3417.   switch (target) {
  3418.   case NOTHING:
  3419.     notify(privs, "No match.");
  3420.     strcpy(buff, "#-1");
  3421.     break;
  3422.   case AMBIGUOUS:
  3423.     notify(privs, "I'm not sure who you mean.");
  3424.     strcpy(buff, "#-2");
  3425.     break;
  3426.   default:
  3427.     sprintf(buff, "#%d", target);
  3428.   }
  3429. }
  3430.  
  3431. FUNCTION(fun_locate)
  3432. {
  3433.   dbref looker;
  3434.   object_flag_type pref_type;
  3435.   dbref item;
  3436.   char *p;
  3437.   int keys = 0;
  3438.   int done = 0;
  3439.   
  3440.   /* find out what we're matching in relation to */
  3441.   looker = match_thing(privs, args[0]);
  3442.   if (looker == NOTHING) {
  3443.     notify(privs, "I don't see that here.");
  3444.     strcpy(buff, "#-1");
  3445.     return;
  3446.   }
  3447.   if (!See_All(privs) && !controls(privs, looker)) {
  3448.     notify(privs, "Permission denied.");
  3449.     strcpy(buff, "#-1");
  3450.     return;
  3451.   }
  3452.  
  3453.   /* find out our preferred match type */
  3454.   pref_type = NOTYPE;
  3455.   for (p = args[2]; *p; p++) {
  3456.     switch (*p) {
  3457.     case 'N':
  3458.       pref_type = NOTYPE;
  3459.       break;
  3460.     case 'E':
  3461.       pref_type = TYPE_EXIT;
  3462.       break;
  3463.     case 'P':
  3464.       pref_type = TYPE_PLAYER;
  3465.       break;
  3466.     case 'R':
  3467.       pref_type = TYPE_ROOM;
  3468.       break;
  3469.     case 'T':
  3470.       pref_type = TYPE_THING;
  3471.       break;
  3472.     case 'L':
  3473.       keys = 1;
  3474.       break;
  3475.     }
  3476.   }
  3477.  
  3478.   if (keys)
  3479.     init_match_check_keys(looker, args[1], pref_type);
  3480.   else
  3481.     init_match(looker, args[1], pref_type);
  3482.  
  3483.   /* now figure out where what kinds of matches we want done */
  3484.   for (p = args[2]; *p && !done; p++) {
  3485.     switch (*p) {
  3486.     case '*':
  3487.       match_everything();
  3488.       done = 1;
  3489.       break;
  3490.     case 'a':
  3491.       match_absolute();
  3492.       break;
  3493.     case 'e':
  3494.       match_exit();
  3495.       break;
  3496.     case 'h':
  3497.       match_here();
  3498.       break;
  3499.     case 'i':
  3500.       match_possession();
  3501.       break;
  3502.     case 'm':
  3503.       match_me();
  3504.       break;
  3505.     case 'n':
  3506.       match_neighbor();
  3507.       break;
  3508.     case 'p':
  3509.       match_player();
  3510.       break;
  3511.     case 'N': case 'E' : case 'P' : case 'R' : case 'T' : case 'L':
  3512.       /* these are from previous type switch check. ignore. */
  3513.       break;
  3514.     default:
  3515.       notify(privs, tprintf("I don't understand switch '%c'.", *p));
  3516.       break;
  3517.     }
  3518.   }
  3519.  
  3520.   /* report the results */
  3521.   item = match_result();
  3522.  
  3523.   if (item == NOTHING)
  3524.     notify(privs, "Nothing found.");
  3525.   else if (item == AMBIGUOUS)
  3526.     notify(privs, "More than one match found.");
  3527.   strcpy(buff, tprintf("#%d", item));
  3528. }
  3529.  
  3530.  
  3531. /* --------------------------------------------------------------------------
  3532.  * Booleans: AND, OR, XOR, NOT
  3533.  */
  3534.  
  3535. FUNCTION(fun_and)
  3536. {
  3537.   sprintf(buff, "%d", translate(args[0]) && translate(args[1]));
  3538. }
  3539.  
  3540. FUNCTION(fun_or)
  3541. {
  3542.   sprintf(buff, "%d", translate(args[0]) || translate(args[1]));
  3543. }
  3544.  
  3545. FUNCTION(fun_not)
  3546. {
  3547.   sprintf(buff, "%d", !translate(args[0]));
  3548. }
  3549.  
  3550. FUNCTION(fun_xor)
  3551. {
  3552.   sprintf(buff, "%d", ((translate(args[0]) && !translate(args[1])) ||
  3553.                (!translate(args[0]) && translate(args[1]))));
  3554. }
  3555.  
  3556. /* --------------------------------------------------------------------------
  3557.  * Complex list handling functions: SHUFFLE, SORT, SETUNION, SETINTER,
  3558.  *   SETDIFF, and auxiliary functions.
  3559.  */
  3560.  
  3561. static void swap(p, q)
  3562.      char **p;
  3563.      char **q;
  3564. {
  3565.   /* swaps two pointers to strings */
  3566.  
  3567.   char *temp;
  3568.   temp = *p;
  3569.   *p = *q;
  3570.   *q = temp;
  3571. }
  3572.  
  3573. FUNCTION(fun_shuffle)
  3574. {
  3575.   /* given a list of words, randomize the order of words. 
  3576.    * We do this by taking each element, and swapping it with another
  3577.    * element with a greater array index (thus, words[0] can be swapped
  3578.    * with anything up to words[n], words[5] with anything between
  3579.    * itself and words[n], etc.
  3580.    * This is relatively fast - linear time - and reasonably random.
  3581.    */
  3582.  
  3583.   char *words[BUFFER_LEN];
  3584.   int n, i, j;
  3585.  
  3586.   /* split the list up, or return if the list is empty */
  3587.   if (!args[0] || !*args[0]) {
  3588.     *buff = '\0';
  3589.     return;
  3590.   }
  3591.   n = list2arr(words, BUFFER_LEN, args[0]);
  3592.  
  3593.   /* shuffle it */
  3594.   for (i = 0; i < n; i++) {
  3595.     j = getrandom(n - i) + i;
  3596.     swap(&words[i], &words[j]);
  3597.   }
  3598.  
  3599.   arr2list(words, n, buff);
  3600. }
  3601.  
  3602. static void do_gensort(s, n, sort_type)
  3603.      char *s[];
  3604.      int n;
  3605.      int sort_type;
  3606. {
  3607.     int i, j;
  3608.  
  3609.     switch (sort_type) {
  3610.       case ALPHANUM_LIST:
  3611.     for (i = 0; i < n; i++)
  3612.         for (j = i+1; j < n; j++)
  3613.         if (strcmp(*(s+i), *(s+j)) > 0)
  3614.             swap(s+i, s+j);
  3615.     break;
  3616.       case NUMERIC_LIST:
  3617.     for (i = 0; i < n; i++) {
  3618.         for (j = i+1; j < n; j++) {
  3619. #ifdef FLOATING_POINTS
  3620.         if (atof(*(s+i)) > atof(*(s+j)))
  3621.             swap(s+i, s+j);
  3622. #else
  3623.         if (atoi(*(s+i)) > atoi(*(s+j)))
  3624.             swap(s+i, s+j);
  3625. #endif                /* FLOATING_POINTS */
  3626.         }
  3627.     }
  3628.     break;
  3629.       case DBREF_LIST:
  3630.     for (i = 0; i < n; i++)
  3631.         for (j = i+1; j < n; j++)
  3632.         if (dbnum(*(s+i)) > dbnum(*(s+j)))
  3633.             swap(s+i, s+j);
  3634.     break;
  3635.     }
  3636. }
  3637.  
  3638. FUNCTION(fun_sort)
  3639. {
  3640.     char list[BUFFER_LEN];
  3641.     char *ptrs[BUFFER_LEN];
  3642.     int nptrs, sort_type;
  3643.  
  3644.     /* Check our arguments. */
  3645.     if (nargs == 0) {
  3646.     *buff = '\0';
  3647.     return;
  3648.     }
  3649.     if (!ok_nargs("SORT", nargs, 1, 3, buff))
  3650.     return;
  3651.  
  3652.     strcpy(list, args[0]);
  3653.     nptrs = list2arr(ptrs, BUFFER_LEN, list);
  3654.     sort_type = get_list_type(args, nargs, 2, ptrs, nptrs);
  3655.     do_gensort(ptrs, nptrs, sort_type);
  3656.     arr2list(ptrs, nptrs, buff);
  3657. }
  3658.  
  3659. static void do_sort(s, n)
  3660.      char *s[];
  3661.      int n;
  3662. {
  3663.   /* uses a transposition sort to sort an array of words */
  3664.  
  3665.   int i, j;                     /* utility */
  3666.  
  3667.   for (i = 0; i < n; i++)
  3668.     for (j = i + 1; j < n; j++)
  3669.       if (strcmp(*(s + i), *(s + j)) > 0)
  3670.         swap(s + i, s + j);
  3671. }
  3672.  
  3673. static void do_setfun(buff, str1, str2, flag)
  3674.      char *buff;
  3675.      char *str1;
  3676.      char *str2;
  3677.      int flag;
  3678. {
  3679.     /* auxiliary functions to handle list sets */
  3680.  
  3681.     char list1[BUFFER_LEN], list2[BUFFER_LEN];
  3682.     char *p, *oldp;
  3683.     char *a1[MAX_ARG];
  3684.     char *a2[MAX_ARG];
  3685.     int n1, n2, x1, x2, val;
  3686.  
  3687.     /* copy the two lists */
  3688.     strcpy(list1, str1);
  3689.     strcpy(list2, str2);
  3690.  
  3691.     /* make arrays out of them */
  3692.     n1 = list2arr(a1, BUFFER_LEN, list1);
  3693.     n2 = list2arr(a2, BUFFER_LEN, list2);
  3694.     
  3695.     /* sort each array */
  3696.     do_sort(a1, n1);
  3697.     do_sort(a2, n2);
  3698.  
  3699.     x1 = x2 = 0;
  3700.     oldp = p = buff;
  3701.     *p = '\0';
  3702.  
  3703.     /* now do the requested operations */
  3704.     switch (flag) {
  3705.       case 1:
  3706.     /* set union */
  3707.  
  3708.     /* handle single-word lists that are the same first */
  3709.  
  3710.     if ((n1 == 1) && (n2 == 1) && !strcmp(a1[0], a2[0])) {
  3711.         safe_str(a1[0], buff, &p);
  3712.         break;
  3713.     }
  3714.  
  3715.     /* loop until done with at least one list */
  3716.  
  3717.     while ((x1 < n1) && (x2 < n2)) {
  3718.         /* skip duplicates */
  3719.  
  3720.         while ((x1 < n1) && !strcmp(a1[x1], oldp))
  3721.         x1++;
  3722.         while ((x2 < n2) && !strcmp(a2[x2], oldp))
  3723.         x2++;
  3724.  
  3725.         /* compare and copy */
  3726.  
  3727.         if ((x1 < n1) && (x2 < n2)) {
  3728.         if (p != buff)
  3729.             safe_chr(' ', buff, &p);
  3730.         oldp = p;
  3731.         if (strcmp(a1[x1], a2[x2]) < 0) {
  3732.             safe_str(a1[x1], buff, &p);
  3733.             x1++;
  3734.         } else {
  3735.             safe_str(a2[x2], buff, &p);
  3736.             x2++;
  3737.         }
  3738.         *p = '\0';
  3739.         }
  3740.     }
  3741.  
  3742.     /* copy remainders, and strip duplicates */
  3743.     for ( ; x1 < n1; x1++) {
  3744.         if (strcmp(oldp, a1[x1])) {
  3745.         if (p != buff)
  3746.             safe_chr(' ', buff, &p);
  3747.         oldp = p;
  3748.         safe_str(a1[x1], buff, &p);
  3749.         *p = '\0';
  3750.         }
  3751.     }
  3752.     for ( ; x2 < n2; x2++) {
  3753.         if (strcmp(oldp, a2[x2])) {
  3754.         if (p != buff)
  3755.             safe_chr(' ', buff, &p);
  3756.         oldp = p;
  3757.         safe_str(a2[x2], buff, &p);
  3758.         *p = '\0';
  3759.         }
  3760.     }
  3761.     break;
  3762.       case 2:
  3763.     /* set intersection */
  3764.  
  3765.     while ((x1 < n1) && (x2 < n2)) {
  3766.         val = strcmp(a1[x1], a2[x2]);
  3767.         if (!val) {
  3768.  
  3769.         /* got a match. copy it */
  3770.  
  3771.         if (p != buff)
  3772.             safe_chr(' ', buff, &p);
  3773.         oldp = p;
  3774.         safe_str(a1[x1], buff, &p);
  3775.         x1++;
  3776.         x2++;
  3777.         while ((x1 < n1) && !strcmp(a1[x1], oldp))
  3778.             x1++;
  3779.         while ((x2 < n2) && !strcmp(a2[x2], oldp))
  3780.             x2++;
  3781.         } else if (val < 0) {
  3782.         x1++;
  3783.         } else {
  3784.         x2++;
  3785.         }
  3786.     }
  3787.     break;
  3788.       case 3:
  3789.     /* set difference */
  3790.  
  3791.     while ((x1 < n1) && (x2 < n2)) {
  3792.         val = strcmp(a1[x1], a2[x2]);
  3793.         if (!val) {
  3794.  
  3795.         /* got a match. increment pointers */
  3796.         
  3797.         oldp = a1[x1];
  3798.         while ((x1 < n1) && !strcmp(a1[x1], oldp))
  3799.             x1++;
  3800.         while ((x2 < n2) && !strcmp(a2[x2], oldp))
  3801.             x2++;
  3802.         } else if (val < 0) {
  3803.  
  3804.         /* found one in first list which isn't in second. copy it */
  3805.  
  3806.         if (p != buff)
  3807.             safe_chr(' ', buff, &p);
  3808.         safe_str(a1[x1], buff, &p);
  3809.         oldp = a1[x1];
  3810.         x1++;
  3811.         while ((x1 < n1) && !strcmp(a1[x1], oldp))
  3812.             x1++;
  3813.  
  3814.         } else {
  3815.  
  3816.         /* found one in second which isn't in first. ignore it */
  3817.  
  3818.         oldp = a2[x2];
  3819.         x2++;
  3820.         while ((x2 < n2) && !strcmp(a2[x2], oldp))
  3821.             x2++;
  3822.         }
  3823.     }
  3824.  
  3825.     /* copy over the elements of the first list that remain */
  3826.  
  3827.     while (x1 < n1) {
  3828.         if (p != buff)
  3829.         safe_chr(' ', buff, &p);
  3830.         safe_str(a1[x1], buff, &p);
  3831.         oldp = a1[x1];
  3832.         x1++;
  3833.         while ((x1 < n1) && !strcmp(a1[x1], oldp))
  3834.         x1++;
  3835.     }
  3836.     break;
  3837.       default:
  3838.     strcpy(buff, "#-1 BAD ARGUMENT TO SETFUN");
  3839.     }
  3840.     *p = '\0';
  3841. }
  3842.  
  3843. FUNCTION(fun_setunion)
  3844. {
  3845.   if (!*args[0] && !*args[1])
  3846.       *buff = '\0';
  3847.   else
  3848.       do_setfun(buff, args[0], args[1], 1);
  3849. }
  3850.  
  3851. FUNCTION(fun_setinter)
  3852. {
  3853.     do_setfun(buff, args[0], args[1], 2);
  3854. }
  3855.  
  3856. FUNCTION(fun_setdiff)
  3857. {
  3858.     do_setfun(buff, args[0], args[1], 3);
  3859. }
  3860.  
  3861. /* --------------------------------------------------------------------------
  3862.  * Creation functions: CREATE, OPEN, DIG
  3863.  */
  3864.  
  3865. FUNCTION(fun_create)
  3866. {
  3867.   sprintf(buff, "#%d", do_create(privs, args[0], atol(args[1])));
  3868. }
  3869.  
  3870. FUNCTION(fun_open) 
  3871. {    
  3872.   sprintf(buff, "#%d", do_real_open(privs, args[0], args[1], NOTHING));
  3873. }
  3874.  
  3875. FUNCTION(fun_dig) 
  3876. {
  3877.   char *argv[MAX_ARG];
  3878.   argv[1] = args[1];
  3879.   argv[2] = args[2];
  3880.   sprintf(buff, "#%d", do_dig(privs, args[0], argv, 0));
  3881. }
  3882.  
  3883. /* --------------------------------------------------------------------------
  3884.  * The actual function handlers
  3885.  */
  3886.  
  3887. typedef struct fun FUN;
  3888.  
  3889. struct fun {
  3890.   const char *name;
  3891.   void (*fun) ();
  3892.   int nargs;
  3893.   int ftype;
  3894. };
  3895.  
  3896. FUN flist[] =
  3897. {
  3898.   {"ABS", fun_abs, 1, FN_REG},
  3899.   {"ADD", fun_add, 2, FN_REG},
  3900.   {"AFTER", fun_after, 2, FN_REG},
  3901.   {"ALPHAMAX", fun_alphamax, -1, FN_REG},
  3902.   {"ALPHAMIN", fun_alphamin, -1, FN_REG},
  3903.   {"AND", fun_and, 2, FN_REG},
  3904.   {"ANDFLAGS", fun_andflags, 2, FN_REG},
  3905.   {"ART", fun_art, 1, FN_REG},
  3906.   {"BEEP", fun_beep, 1, FN_REG},
  3907.   {"BEFORE", fun_before, 2, FN_REG},
  3908.   {"CAPSTR", fun_capstr, 1, FN_REG},
  3909.   {"CAT", fun_cat, -1, FN_REG},
  3910.   {"COMP", fun_comp, 2, FN_REG},
  3911.   {"CON", fun_con, 1, FN_REG},
  3912.   {"CONN", fun_conn, 1, FN_REG},
  3913.   {"CONTROLS", fun_controls, 2, FN_REG},
  3914.   {"CONVSECS", fun_convsecs, 1, FN_REG},
  3915.   {"CONVTIME", fun_convtime, 1, FN_REG},
  3916.   {"CREATE", fun_create, 2, FN_REG},
  3917.   {"DELETE", fun_delete, -1, FN_REG},
  3918.   {"DIE", fun_die, 2, FN_REG},
  3919.   {"DIG", fun_dig, 3, FN_REG},
  3920.   {"DIST2D", fun_dist2d, 4, FN_REG},
  3921.   {"DIST3D", fun_dist3d, 6, FN_REG},
  3922.   {"DIV", fun_div, 2, FN_REG},
  3923.   {"EDIT", fun_edit, 3, FN_REG},
  3924.   {"ELEMENT", fun_element, 3, FN_REG},
  3925.   {"ELOCK", fun_elock, 2, FN_REG},
  3926.   {"EQ", fun_eq, 2, FN_REG},
  3927.   {"EVAL", fun_eval, 2, FN_REG},
  3928.   {"ESCAPE", fun_escape, 1, FN_REG},
  3929.   {"EXIT", fun_exit, 1, FN_REG},
  3930.   {"EXTRACT", fun_extract, 3, FN_REG},
  3931.   {"FILTER", fun_filter, 2, FN_REG},
  3932.   {"FIRST", fun_first, 1, FN_REG},
  3933.   {"FLAGS", fun_flags, 1, FN_REG},
  3934.   {"FLIP", fun_flip, 1, FN_REG},
  3935.   {"FOLD", fun_fold, -1, FN_REG},
  3936.   {"FULLNAME", fun_fullname, 1, FN_REG},
  3937.   {"GET", fun_get, 1, FN_REG},
  3938.   {"GET_EVAL", fun_ufun, -1, FN_REG},
  3939.   {"GREP", fun_grep, 3, FN_REG},
  3940.   {"GT", fun_gt, 2, FN_REG},
  3941.   {"GTE", fun_gte, 2, FN_REG},
  3942.   {"HASFLAG", fun_hasflag, 2, FN_REG},
  3943.   {"HOME", fun_home, 1, FN_REG},
  3944.   {"IDLE", fun_idlesecs, 1, FN_REG},
  3945.   {"IDLESECS", fun_idlesecs, 1, FN_REG},
  3946.   {"INDEX", fun_index, 4, FN_REG},
  3947.   {"INSERT", fun_insert, -1, FN_REG},
  3948.   {"ISNUM", fun_isnum, 1, FN_REG},
  3949.   {"ISWORD", fun_isword, 1, FN_REG},
  3950.   {"ITER", fun_iter, 2, FN_NOPARSE},
  3951.   {"ITEMS", fun_items, 2, FN_REG},
  3952.   {"LATTR", fun_lattr, 1, FN_REG},
  3953.   {"LCON", fun_lcon, 1, FN_REG},
  3954.   {"LCSTR", fun_lcstr, 1, FN_REG},
  3955.   {"LEXITS", fun_lexits, 1, FN_REG},
  3956.   {"LJUST", fun_ljust, -1, FN_REG},
  3957.   {"LNUM", fun_lnum, 1, FN_REG},
  3958.   {"LOC", fun_loc, 1, FN_REG},
  3959.   {"LOCATE", fun_locate, 3, FN_REG},
  3960.   {"LOCK", fun_lock, 1, FN_REG},
  3961.   {"LSEARCH", fun_lsearch, 3, FN_REG},
  3962.   {"LSTATS", fun_lstats, 1, FN_REG},
  3963.   {"LT", fun_lt, 2, FN_REG},
  3964.   {"LTE", fun_lte, 2, FN_REG},
  3965.   {"LWHO", fun_lwho, 0, FN_REG},
  3966. #ifdef USE_MAILER
  3967.   {"MAIL", fun_mail, -1, FN_REG},
  3968. #endif
  3969.   {"MATCH", fun_match, 2, FN_REG},
  3970.   {"MAX", fun_max, -1, FN_REG},
  3971.   {"MEMBER", fun_member, 2, FN_REG},
  3972.   {"MERGE", fun_merge, 3, FN_REG},
  3973.   {"MID", fun_mid, 3, FN_REG},
  3974.   {"MIN", fun_min, -1, FN_REG},
  3975.   {"MOD", fun_mod, 2, FN_REG},
  3976.   {"MONEY", fun_money, 1, FN_REG},
  3977.   {"MUL", fun_mul, 2, FN_REG},
  3978.   {"NAME", fun_name, 1, FN_REG},
  3979.   {"NEARBY", fun_nearby, 2, FN_REG},
  3980.   {"NEQ", fun_neq, 2, FN_REG},
  3981.   {"NEXT", fun_next, 1, FN_REG},
  3982.   {"NOT", fun_not, 1, FN_REG},
  3983.   {"NUM", fun_num, 1, FN_REG},
  3984.   {"OBJ", fun_obj, 1, FN_REG},
  3985.   {"OPEN", fun_open, 2, FN_REG},
  3986.   {"OR", fun_or, 2, FN_REG},
  3987.   {"ORFLAGS", fun_orflags, 2, FN_REG},
  3988.   {"OWNER", fun_owner, 1, FN_REG},
  3989.   {"PARENT", fun_parent, 1, FN_REG},
  3990.   {"PMATCH", fun_pmatch, 1, FN_REG},
  3991.   {"POS", fun_pos, 2, FN_REG},
  3992.   {"POSS", fun_poss, 1, FN_REG},
  3993.   {"R", fun_r, 1, FN_REG},
  3994.   {"RAND", fun_rand, 1, FN_REG},
  3995.   {"REMOVE", fun_remove, 2, FN_REG},
  3996.   {"REPEAT", fun_repeat, 2, FN_REG},
  3997.   {"REPLACE", fun_replace, -1, FN_REG},
  3998.   {"REST", fun_rest, 1, FN_REG},
  3999.   {"REVERSE", fun_flip, 1, FN_REG},
  4000.   {"REVWORDS", fun_revwords, 1, FN_REG},
  4001.   {"RJUST", fun_rjust, -1, FN_REG},
  4002. #ifdef DO_GLOBALS
  4003.   {"RNUM", fun_rnum, 2, FN_REG},
  4004. #endif
  4005.   {"ROOM", fun_room, 1, FN_REG},
  4006.   {"S", fun_s, 1, FN_REG},
  4007.   {"SECS", fun_secs, 0, FN_REG},
  4008.   {"SECURE", fun_secure, 1, FN_REG},
  4009.   {"SETQ", fun_setq, 2, FN_REG},
  4010.   {"SETDIFF", fun_setdiff, 2, FN_REG},
  4011.   {"SETINTER", fun_setinter, 2, FN_REG},
  4012.   {"SETUNION", fun_setunion, 2, FN_REG},
  4013.   {"SHUFFLE", fun_shuffle, 1, FN_REG},
  4014.   {"SIGN", fun_sign, 1, FN_REG},
  4015.   {"SORT", fun_sort, -1, FN_REG},
  4016.   {"SPACE", fun_space, 1, FN_REG},
  4017.   {"SPLICE", fun_splice, 3, FN_REG},
  4018.   {"SQUISH", fun_squish, 1, FN_REG},
  4019.   {"SCRAMBLE", fun_scramble, 1, FN_REG},
  4020.   {"STRCAT", fun_strcat, 2, FN_REG},
  4021.   {"STRLEN", fun_strlen, 1, FN_REG},
  4022.   {"STRMATCH", fun_strmatch, 2, FN_REG},
  4023.   {"SUB", fun_sub, 2, FN_REG},
  4024.   {"SUBJ", fun_subj, 1, FN_REG},
  4025.   {"SWITCH", fun_switch, -1, FN_NOPARSE},
  4026.   {"TIME", fun_time, 0, FN_REG},
  4027.   {"TRUNC", fun_trunc, 1, FN_REG},
  4028.   {"TYPE", fun_type, 1, FN_REG},
  4029.   {"UCSTR", fun_ucstr, 1, FN_REG},
  4030.   {"UFUN", fun_ufun, -1, FN_REG},
  4031.   {"U", fun_ufun, -1, FN_REG},
  4032.   {"V", fun_v, 1, FN_REG},
  4033.   {"VAL", fun_trunc, 1, FN_REG},
  4034.   {"VISIBLE", fun_visible, 2, FN_REG},
  4035.   {"WHERE", fun_where, 1, FN_REG},
  4036.   {"WORDPOS", fun_wordpos, 2, FN_REG},
  4037.   {"WORDS", fun_words, 1, FN_REG},
  4038.   {"XGET", fun_xget, 2, FN_REG},
  4039.   {"XOR", fun_xor, 2, FN_REG},
  4040.   {"ZFUN", fun_zfun, -1, FN_REG},
  4041.   {"ZONE", fun_zone, 1, FN_REG},
  4042. #ifdef FLOATING_POINTS
  4043.   {"ACOS", fun_acos, 1, FN_REG},
  4044.   {"ASIN", fun_asin, 1, FN_REG},
  4045.   {"ATAN", fun_atan, 1, FN_REG},
  4046.   {"CEIL", fun_ceil, 1, FN_REG},
  4047.   {"COS", fun_cos, 1, FN_REG},
  4048.   {"E", fun_e, 0, FN_REG},
  4049.   {"EXP", fun_exp, 1, FN_REG},
  4050.   {"FDIV", fun_fdiv, 2, FN_REG},
  4051.   {"FLOOR", fun_floor, 1, FN_REG},
  4052.   {"LOG", fun_log, 1, FN_REG},
  4053.   {"LN", fun_ln, 1, FN_REG},
  4054.   {"PI", fun_pi, 0, FN_REG},
  4055.   {"POWER", fun_power, 2, FN_REG},
  4056.   {"ROUND", fun_round, 2, FN_REG},
  4057.   {"SIN", fun_sin, 1, FN_REG},
  4058.   {"TAN", fun_tan, 1, FN_REG},
  4059. #endif                /* FLOATING_POINTS */
  4060.   {NULL, NULL, 0, 0}
  4061. };
  4062.  
  4063. void do_list_functions(player)
  4064.      dbref player;
  4065. {
  4066.   /* lists all built-in functions. */
  4067.  
  4068.   FUN *fp;
  4069.   char buff[BUFFER_LEN];
  4070.   char *bp;
  4071.  
  4072.   bp = buff;
  4073.   safe_str("Functions:", buff, &bp);
  4074.  
  4075.   for (fp = flist; fp->name != NULL; fp++) {
  4076.     safe_chr(' ', buff, &bp);
  4077.     safe_str(fp->name, buff, &bp);
  4078.   }
  4079.   *bp = '\0';
  4080.  
  4081.   notify(player, buff);
  4082. }
  4083.  
  4084. /*---------------------------------------------------------------------------
  4085.  * Hashed function table stuff
  4086.  */
  4087.  
  4088. #define FUNC_HASH_SIZE  128
  4089. #define FUNC_HASH_MASK  127
  4090.  
  4091. struct ftab_entry {
  4092.   struct ftab_entry *next;
  4093.   char *name;
  4094.   FUN *fp;
  4095. };
  4096.  
  4097. static struct ftab_entry *func_hashtab[FUNC_HASH_SIZE];
  4098. static FUN *null_htab;
  4099.  
  4100. FUN *func_hash_lookup(name)
  4101.      char *name;
  4102. {
  4103.   struct ftab_entry *p;
  4104.  
  4105.   for (p = func_hashtab[hash_fn(name, FUNC_HASH_MASK)];
  4106.        p != NULL; p = p->next)
  4107.     if (!strcmp(name, p->name))
  4108.       return p->fp;
  4109.  
  4110.   return null_htab;        /* the notfound pointer */
  4111. }
  4112.  
  4113. void func_hash_insert(name, func)
  4114.      char *name;
  4115.      FUN *func;
  4116. {
  4117.   struct ftab_entry *newp;
  4118.   unsigned hashval;
  4119.  
  4120.   /* can assume no duplicates */
  4121.   newp = (struct ftab_entry *) malloc(sizeof(struct ftab_entry));
  4122.   newp->name = (char *) strdup(name);
  4123.   newp->fp = func;
  4124.   hashval = hash_fn(name, FUNC_HASH_MASK);
  4125.   newp->next = func_hashtab[hashval];
  4126.   func_hashtab[hashval] = newp;
  4127. }
  4128.  
  4129. void init_func_hashtab()
  4130. {
  4131.   FUN *fp;
  4132.   
  4133.   for (fp = flist; fp->name; fp++)
  4134.     func_hash_insert((char *)fp->name, (FUN *)fp);
  4135.  
  4136.   /* treat null function as a special case */
  4137.   null_htab = (FUN *) malloc(sizeof(FUN));
  4138.   null_htab->name = NULL;
  4139.   null_htab->fun = NULL;
  4140.   null_htab->nargs = 0;
  4141. }
  4142.  
  4143.  
  4144. /*-------------------------------------------------------------------------
  4145.  * Function handlers and the other good stuff. Almost all this code is
  4146.  * a modification of TinyMUSH 2.0 code.
  4147.  */
  4148.  
  4149. int extra_arglist(str, fargs, nargs)
  4150.      char *str;
  4151.      char *fargs[];
  4152.      int nargs;
  4153. {
  4154.     /* parses apart a comma-separated list, and returns the number of
  4155.      * arguments obtained. The list should already be pronoun-substituted.
  4156.      * str is destructively modified. No memory is allocated. No more than
  4157.      * nargs arguments are made. The list elements are placed in fargs.
  4158.      */
  4159.  
  4160.     int i;
  4161.  
  4162.     for (i = 0; i < nargs; i++)
  4163.     fargs[i] = NULL;
  4164.     if (!str || !*str)
  4165.     return 0;
  4166.  
  4167.     i = 0;
  4168.     while ((i < nargs) && str) {
  4169.     if (i < (nargs - 1))
  4170.         fargs[i] = parse_to(&str, ',', 0);
  4171.     else
  4172.         fargs[i] = parse_to(&str, '\0', 0);
  4173.     i++;
  4174.     }
  4175.     return i;
  4176. }
  4177.  
  4178. int get_gender(player)
  4179.      dbref player;
  4180. {
  4181.   /* 0 for error, 1 for neuter, 2 for female, 3 for male */
  4182.  
  4183.   ATTR *a;
  4184.  
  4185.   a = atr_get(player, "SEX");
  4186.  
  4187.   if (!a)
  4188.     return 1;
  4189.  
  4190.   switch (*uncompress(a->value)) {
  4191.   case 'M':
  4192.   case 'm':
  4193.     return 3;
  4194.   case 'f':
  4195.   case 'F':
  4196.     return 2;
  4197.   default:
  4198.     return 1;
  4199.   }
  4200. }
  4201.  
  4202. #define NEXTCHAR \
  4203.    if (cstr == zstr) { \
  4204.      cstr++; \
  4205.      zstr++; \
  4206.    } else \
  4207.      *zstr++ = *cstr++
  4208.  
  4209. char *parse_to(dstr, delim, eflags)
  4210.      char **dstr;
  4211.      char delim;
  4212.      int eflags;
  4213. {
  4214.   /* Split up a line at a character, but obey nesting. This will hack
  4215.    * up the string (null replaces the delimiter)
  4216.    * str will be modified to point to the character after the delimiter. 
  4217.    * The function will return a pointer to the found string.
  4218.    * If we don't find the delimiter, str is returned as NULL.
  4219.    */
  4220.  
  4221. #define MAX_STACK 32
  4222.  
  4223.   char stack[MAX_STACK];
  4224.   char *rstr, *cstr, *zstr;
  4225.   int sp, tp, first, brlev;
  4226.  
  4227.   if (!dstr || !*dstr)
  4228.     return NULL;
  4229.  
  4230.   if (**dstr == '\0') {
  4231.     rstr = *dstr;
  4232.     *dstr = NULL;
  4233.     return rstr;
  4234.   }
  4235.  
  4236.   sp = 0;
  4237.   first = 1;
  4238.   rstr = *dstr;
  4239.   while (*rstr && isspace(*rstr))
  4240.     rstr++;
  4241.   *dstr = rstr;
  4242.   zstr = cstr = rstr;
  4243.  
  4244.   while (*cstr) {
  4245.     switch (*cstr) {
  4246.  
  4247.     case '\\':            /* escape char */
  4248.     case '%':            /* can be used as an escape char */
  4249.       first = 0;
  4250.       NEXTCHAR;
  4251.       if (*cstr)
  4252.     NEXTCHAR;
  4253.       break;
  4254.  
  4255.     case ']':
  4256.     case ')':
  4257.       first = 0;
  4258.       for (tp = sp - 1; (tp >= 0) && (stack[tp] != *cstr); tp--)
  4259.     ;
  4260.       /* If we've hit something on a stack, go back to it.
  4261.        * Otherwise, if it's our delimiter, we're done (replace
  4262.        * delim with null, and return a pointer to the char after
  4263.        * it). If it's not, then just move over it.
  4264.        */
  4265.       if (tp >= 0)
  4266.     sp = tp;
  4267.       else
  4268.     if (*cstr == delim) {
  4269.       if (!first && (cstr[-1] == ' '))
  4270.         zstr--;
  4271.       *zstr = '\0';
  4272.       *dstr = ++cstr;
  4273.       return rstr;
  4274.     }
  4275.       NEXTCHAR;
  4276.       break;
  4277.  
  4278.     case '{':
  4279.       first = 0;
  4280.       brlev = 1;
  4281.       if (eflags & EV_STRIP) {
  4282.     cstr++;
  4283.       } else {
  4284.     NEXTCHAR;
  4285.       }
  4286.       while (*cstr && (brlev > 0)) {
  4287.     switch (*cstr) {
  4288.     case '\\':
  4289.     case '%':
  4290.       if (cstr[1]) {
  4291.         NEXTCHAR;
  4292.       }
  4293.       break;
  4294.     case '{':
  4295.       brlev++;
  4296.       break;
  4297.     case '}':
  4298.       brlev--;
  4299.       break;
  4300.     }
  4301.     if (brlev > 0) {
  4302.       NEXTCHAR;
  4303.     }
  4304.       }
  4305.       if ((eflags & EV_STRIP) && (brlev == 0)) {
  4306.     cstr++;
  4307.       } else if (brlev == 0) {
  4308.     NEXTCHAR;
  4309.       }
  4310.       break;
  4311.  
  4312.     default:
  4313.       if ((*cstr == delim) && (sp == 0)) {
  4314.     if (!first && (cstr[-1] == ' '))
  4315.       zstr--;
  4316.     *zstr = '\0';
  4317.     *dstr = ++cstr;
  4318.     return rstr;
  4319.       }
  4320.       switch (*cstr) {
  4321.       case ' ':            /* just a space. kill extra ones. */
  4322.     if (first)
  4323.       rstr++;
  4324.     else if (cstr[-1] == ' ')
  4325.       zstr--;
  4326.     break;
  4327.       case '[':
  4328.     first = 0;
  4329.     if (sp < MAX_STACK)
  4330.       stack[sp++] = ']';
  4331.     break;
  4332.       case '(':
  4333.     first = 0;
  4334.     if (sp < MAX_STACK)
  4335.       stack[sp++] = ')';
  4336.     break;
  4337.       default:
  4338.     first = 0;
  4339.       }
  4340.       NEXTCHAR;
  4341.     }
  4342.   }
  4343.  
  4344.   if (!first && (cstr[-1] == ' '))
  4345.     zstr--;
  4346.   *zstr = '\0';
  4347.   *dstr = NULL;
  4348.   return rstr;
  4349. }
  4350.  
  4351. char *parse_arglist(player, cause, dstr, delim, eflags, fargs, nfargs)
  4352.      dbref player;
  4353.      dbref cause;
  4354.      char *dstr;
  4355.      char delim;
  4356.      int eflags;
  4357.      char *fargs[];
  4358.      int nfargs;
  4359. {
  4360.   /* Parses a line of comma-separated strings into an argument list.
  4361.    * A pointer is returned to whatever follows the final delimiter
  4362.    * (or NULL if the arglist is unterminated). 
  4363.    * The original arglist (in dstr) is hacked up.
  4364.    * This function allocates memory for the fargs, which must be freed.
  4365.    */
  4366.  
  4367.   char *rstr, *tstr;
  4368.   int arg, peval;
  4369.  
  4370.   for (arg = 0; arg < nfargs; arg++)
  4371.     fargs[arg] = NULL;
  4372.  
  4373.   if (!dstr)
  4374.     return NULL;
  4375.  
  4376.   rstr = parse_to(&dstr, delim, 0);
  4377.   arg = 0;
  4378.  
  4379.   if (eflags & EV_EVAL)
  4380.     peval = 0;
  4381.   else
  4382.     peval = eflags;
  4383.  
  4384.   while ((arg < nfargs) && rstr) {
  4385.     if (arg < (nfargs - 1))
  4386.       tstr = parse_to(&rstr, ',', peval);
  4387.     else 
  4388.       tstr = parse_to(&rstr, '\0', peval);
  4389.     if (eflags & EV_EVAL) {
  4390.       fargs[arg] = exec(player, cause, eflags | EV_FCHECK, tstr);
  4391.     } else {
  4392.       fargs[arg] = (char *) malloc(BUFFER_LEN + 1);
  4393. #ifdef MEM_CHECK
  4394.       add_check("exec.buff");
  4395. #endif
  4396.       strcpy(fargs[arg], tstr);
  4397.     }
  4398.     arg++;
  4399.   }
  4400.   
  4401.   return dstr;
  4402. }
  4403.  
  4404. char *exec(player, cause, eflags, str)
  4405.     dbref player;
  4406.     dbref cause;
  4407.     int eflags;
  4408.     char *str;
  4409. {
  4410.   /* Function and other substitution evaluation.
  4411.    * This function mallocs memory, which must be freed.
  4412.    */
  4413.  
  4414.   char *fargs[10];
  4415.   char *buff, *bufc, *tstr, *tbuf, *tbufc, *savepos;
  4416.   char savec, ch;
  4417.   int at_space, nfargs, gender, i, j, done;
  4418.   FUN *fp;
  4419.   ATTR *attrib;
  4420.   char temp[32];
  4421.   int a;
  4422.   char dbuf[BUFFER_LEN];
  4423.   char *dp, *dpexec;
  4424.   char dsave[BUFFER_LEN];
  4425.  
  4426.   static const char *subj[4] = {"", "it", "she", "he"};
  4427.   static const char *poss[4] = {"", "its", "her", "his"};
  4428.   static const char *obj[4] = {"", "it", "her", "him"};
  4429.  
  4430.   if (str == NULL)
  4431.     return NULL;
  4432.  
  4433.   for (a = 0; a < 10; a++)
  4434.     fargs[a] = NULL;
  4435.   
  4436.   buff = (char *) malloc(BUFFER_LEN + 1);
  4437. #ifdef MEM_CHECK
  4438.   add_check("exec.buff");
  4439. #endif
  4440.   bufc = buff;
  4441.  
  4442.   at_space = 1;
  4443.   gender = -1;
  4444.   done = 0;
  4445.  
  4446.   if (Debug(player)) {
  4447.     dp = dbuf;
  4448.     safe_str(tprintf("#%d! %s => ", player, str), dbuf, &dp);
  4449.     Flags(player) &= ~DEBUGGING;
  4450.     strcpy(dsave, str);
  4451.     dpexec = exec(player, cause, eflags, dsave);
  4452.     safe_str(dpexec, dbuf, &dp);
  4453.     *dp = '\0';
  4454.     free(dpexec);
  4455.     Flags(player) |= DEBUGGING;
  4456.     raw_notify(Owner(player), dbuf);
  4457.   }
  4458.  
  4459.   while (*str && !done) {
  4460.     switch (*str) {
  4461.  
  4462.     case ' ':
  4463.       /* Just a space. Add a space if previous char wasn't a space */
  4464.       if (!at_space) {
  4465.     safe_chr(' ', buff, &bufc);
  4466.     at_space = 1;
  4467.       }
  4468.       break;
  4469.  
  4470.     case '\\':
  4471.       /* The escape char. Add the next char without processing it. */
  4472.       at_space = 0;
  4473.       str++;
  4474.       if (*str)
  4475.     safe_chr(*str, buff, &bufc);
  4476.       else
  4477.     str--;
  4478.       break;
  4479.  
  4480.     case '[':
  4481.       /* Beginning of a function. Evaluate the contents of this
  4482.        * as a function. If we find no closing bracket, just add
  4483.        * the '[' and go on.
  4484.        */
  4485.       at_space = 0;
  4486.       tstr = str++;
  4487.       tbuf = parse_to(&str, ']', 0);
  4488.       if (str == NULL) {
  4489.     safe_chr('[', buff, &bufc);
  4490.     str = tstr;
  4491.       } else {
  4492.     tstr = exec(player, cause, eflags | EV_FCHECK | EV_FMAND, tbuf);
  4493.     safe_str(tstr, buff, &bufc);
  4494.     free(tstr);
  4495. #ifdef MEM_CHECK
  4496.     del_check("exec.buff");
  4497. #endif
  4498.     str--;
  4499.       }
  4500.       break;
  4501.  
  4502.     case '{':
  4503.       /* Start of something. Copy everything up to the terminating '}',
  4504.        * don't parse, unless there's no closing brace, in which case
  4505.        * we just add the '{'.
  4506.        */
  4507.       at_space = 0;
  4508.       tstr = str++;
  4509.       tbuf = parse_to(&str, '}', 0);
  4510.       if (str == NULL) {
  4511.     safe_chr('{', buff, &bufc);
  4512.     str = tstr;
  4513.       } else {
  4514.     if (!(eflags & EV_STRIP)) {
  4515.       safe_chr('{', buff, &bufc);
  4516.     }
  4517.         tstr = exec(player, cause, (eflags & ~(EV_STRIP | EV_FCHECK)), tbuf);
  4518.     safe_str(tstr, buff, &bufc);
  4519.     if (!(eflags & EV_STRIP)) {
  4520.       safe_chr('}', buff, &bufc);
  4521.     }
  4522.     free(tstr);
  4523. #ifdef MEM_CHECK
  4524.     del_check("exec.buff");
  4525. #endif
  4526.     str--;
  4527.       }
  4528.       break;
  4529.  
  4530.     case '%':
  4531.       /* Start of percent substitution. Evaluate the chars following,
  4532.        * doing the substitution.
  4533.        */
  4534.       at_space = 0;
  4535.       str++;
  4536.       savec = *str;
  4537.       savepos = bufc;
  4538.       switch (savec) {
  4539.  
  4540.       case '\0':        /* Null -- we're done */
  4541.     str--;
  4542.     break;
  4543.  
  4544.       case '%':            /* Sequence is '%%', so a real percent */
  4545.     safe_chr('%', buff, &bufc);
  4546.     break;
  4547.  
  4548.       case 'r':            /* Newline */
  4549.       case 'R':
  4550.     safe_str((char *)"\r\n", buff, &bufc);
  4551.     break;
  4552.       case 't':            /* Tab */
  4553.       case 'T':
  4554.     safe_chr('\t', buff, &bufc);
  4555.     break;
  4556.       case 'b':            /* Blank space */
  4557.       case 'B':
  4558.     safe_chr(' ', buff, &bufc);
  4559.     break;
  4560.  
  4561.       case '0':            /* stack */
  4562.       case '1':
  4563.       case '2':
  4564.       case '3':
  4565.       case '4':
  4566.       case '5':
  4567.       case '6':
  4568.       case '7':
  4569.       case '8':
  4570.       case '9':
  4571.     i = (*str - '0');
  4572.     if (wptr[i] != NULL)
  4573.       safe_str(wptr[i], buff, &bufc);
  4574.     break;
  4575.  
  4576.       case 'v':            /* variable argument */
  4577.       case 'V':
  4578.       case 'w':
  4579.       case 'W':
  4580.       case 'x':
  4581.       case 'X':
  4582.     str++;
  4583.     if (!*str)
  4584.       str--;
  4585.     ch = UPCASE(*str);
  4586.     if (!isalpha(ch))
  4587.       break;
  4588.     attrib = atr_get(player, tprintf("%c%c", UPCASE(*(str-1)), ch));
  4589.     if (attrib)
  4590.       safe_str(uncompress(attrib->value), buff, &bufc);
  4591.     break;
  4592.  
  4593.       case 'o':            /* objective pronoun */
  4594.       case 'O':
  4595.     if (gender < 0)
  4596.       gender = get_gender(cause);
  4597.     tbuf = (char *) obj[gender];
  4598.     safe_str(tbuf, buff, &bufc);
  4599.     break;
  4600.       case 'p':
  4601.       case 'P':            /* possessive pronoun */
  4602.     if (gender < 0)
  4603.       gender = get_gender(cause);
  4604.     tbuf = (char *) poss[gender];
  4605.     safe_str(tbuf, buff, &bufc);
  4606.     break;
  4607.       case 's':            /* subjective pronoun */
  4608.       case 'S':
  4609.     if (gender < 0)
  4610.       gender = get_gender(cause);
  4611.     tbuf = (char *) subj[gender];
  4612.     safe_str(tbuf, buff, &bufc);
  4613.     break;
  4614.  
  4615.       case 'n':            /* name of enactor */
  4616.       case 'N':
  4617.     safe_str(Name(cause), buff, &bufc);
  4618.     break;
  4619.  
  4620.       case '#':            /* enactor dbref */
  4621.     sprintf(temp, "#%d", cause);
  4622.     safe_str(temp, buff, &bufc);
  4623.     break;
  4624.       case '!':            /* executor ("me") dbref */
  4625.     sprintf(temp, "#%d", player);
  4626.     safe_str(temp, buff, &bufc);
  4627.     break;
  4628.  
  4629.       case 'l':            /* location of enactor */
  4630.       case 'L':
  4631.     /* this does not violate security because you have to trigger
  4632.          * something somehow (for example, by a $command) in order for
  4633.      * it to find your location.
  4634.      */
  4635.     sprintf(temp, "#%d", getloc(cause));
  4636.     safe_str(temp, buff, &bufc);
  4637.     break;
  4638.  
  4639.       default:            /* just copy */
  4640.     safe_chr(*str, buff, &bufc);
  4641.       }
  4642.  
  4643.       if (isupper(savec))
  4644.       *savepos = UPCASE(*savepos);
  4645.       break;
  4646.  
  4647.     case '(':
  4648.       /* start of argument list. Check to see if the stuff before is
  4649.        * a function. If so, execute it (if we should).
  4650.        */
  4651.  
  4652.       at_space = 0;
  4653.       if (!(eflags & EV_FCHECK)) {
  4654.       safe_chr('(', buff, &bufc);
  4655.       break;
  4656.       }
  4657.       eflags &= ~EV_FCHECK;
  4658.  
  4659.       /* get uppercase version of the function name, check to see if
  4660.        * it exists.
  4661.        */
  4662.       *bufc = '\0';
  4663.       tbuf = (char *) malloc(SBUF_LEN + 1);
  4664. #ifdef MEM_CHECK
  4665.       add_check("exec.function_name");
  4666. #endif
  4667.       tbufc = tbuf;
  4668.       safe_short_str(buff, tbuf, &tbufc);
  4669.       *tbufc = '\0';
  4670.       while ((--tbufc >= tbuf) && isspace(*tbufc))
  4671.       ;
  4672.       tbufc++;
  4673.       for (tbufc = tbuf; *tbufc; tbufc++)
  4674.       *tbufc = UPCASE(*tbufc);
  4675.       fp = func_hash_lookup(tbuf);
  4676.  
  4677.       /* give error message if function not found */
  4678.       if (!fp->name) {
  4679.       if (eflags & EV_FMAND) {
  4680.           bufc = buff;
  4681.           safe_str((char *) "#-1 FUNCTION (", buff, &bufc);
  4682.           safe_str(tbuf, buff, &bufc);
  4683.           safe_str((char *) ") NOT FOUND", buff, &bufc);
  4684.           done = 1;
  4685.       } else {
  4686.           safe_chr('(', buff, &bufc);
  4687.       }
  4688.       free(tbuf);
  4689. #ifdef MEM_CHECK
  4690.       del_check("exec.function_name");
  4691. #endif
  4692.       break;
  4693.       }
  4694.       free(tbuf);
  4695. #ifdef MEM_CHECK
  4696.       del_check("exec.function_name");
  4697. #endif
  4698.  
  4699.       /* get the arglist */
  4700.       nfargs = 10;
  4701.       tstr = (char *) str;
  4702.       str = parse_arglist(player, cause, str + 1, ')', 
  4703.               (fp->ftype == FN_NOPARSE) ? 
  4704.               (eflags & ~EV_EVAL) : (eflags | EV_EVAL), 
  4705.               fargs, nfargs);
  4706.  
  4707.       /* if there's no closing delimiter, just add the '(' and continue */
  4708.       if (!str) {
  4709.       str = tstr;
  4710.       safe_chr(*str, buff, &bufc);
  4711.       for (i = 0; i < nfargs; i++)
  4712.           if (fargs[i] != NULL) {
  4713.           free(fargs[i]);
  4714. #ifdef MEM_CHECK
  4715.           del_check("exec.buff");
  4716. #endif
  4717.           }
  4718.       break;
  4719.       }
  4720.  
  4721.       /* Count number of args returned */
  4722.       str--;
  4723.       j = 0;
  4724.       for (i = 0; i < nfargs; i++)
  4725.       if (fargs[i] != NULL)
  4726.           j = i + 1;
  4727.       nfargs = j;
  4728.  
  4729.       /* If we have the right number of args, eval the function.
  4730.        * Otherwise, return an error message.
  4731.        * Special case: zero args is return by parse_arglist as
  4732.        * one null arg.
  4733.        */
  4734.       if ((fp->nargs == 0) && (nfargs == 1)) {
  4735.       if (!*fargs[0]) {
  4736.           free(fargs[0]);
  4737. #ifdef MEM_CHECK
  4738.           del_check("exec.buff");
  4739. #endif
  4740.           fargs[0] = NULL;
  4741.           nfargs = 0;
  4742.       }
  4743.       }
  4744.  
  4745.       if ((nfargs == fp->nargs) || (fp->nargs == -1)) {
  4746.       /* Check recursion limit first */
  4747.       recurs_lev++;
  4748.       invok_counter++;
  4749.       if (recurs_lev >= MAX_NEST_LEVEL)
  4750.           strcpy(buff, "#-1 FUNCTION RECURSION LIMIT EXCEEDED");
  4751.       else if (invok_counter > MAX_NEST_LEVEL * 100)
  4752.           strcpy(buff, "#-1 FUNCTION INVOCATION LIMIT EXCEEDED");
  4753.       else if (invok_counter < MAX_NEST_LEVEL * 100) {
  4754.           if (fp->fun != fun_gfun)
  4755.           fp->fun(buff, fargs, nfargs, player, cause);
  4756.           else
  4757.           fp->fun(buff, fargs, nfargs, player, cause,
  4758.               GF_Index(fp->ftype));
  4759.       } else {
  4760.           *bufc = '\0';
  4761.       }
  4762.       for (bufc = buff; *bufc; bufc++) /* fix bufc */
  4763.           ;            
  4764.       recurs_lev--;        /* don't decrement the invok_counter! */
  4765.       } else {
  4766.       bufc = buff;
  4767.       tstr = (char *) malloc(SBUF_LEN + 1);
  4768. #ifdef MEM_CHECK
  4769.       add_check("exec.numargs");
  4770. #endif
  4771.       sprintf(tstr, "%d", fp->nargs);
  4772.       safe_str((char *) "#-1 FUNCTION (", buff, &bufc);
  4773.       safe_str((char *) fp->name, buff, &bufc);
  4774.       safe_str((char *) ") EXPECTS ", buff, &bufc);
  4775.       safe_str(tstr, buff, &bufc);
  4776.       safe_str((char *) " ARGUMENTS", buff, &bufc);
  4777.       free(tstr);
  4778. #ifdef MEM_CHECK
  4779.       del_check("exec.numargs");
  4780. #endif
  4781.       }
  4782.  
  4783.       /* free up the space allocated for the args */
  4784.       for (i = 0; i < nfargs; i++)
  4785.       if (fargs[i] != NULL) {
  4786.           free(fargs[i]);
  4787. #ifdef MEM_CHECK
  4788.           del_check("exec.buff");
  4789. #endif
  4790.       }
  4791.       break;
  4792.  
  4793.     default:
  4794.       /* just another character. copy it. */
  4795.       at_space = 0;
  4796.       safe_chr(*str, buff, &bufc);
  4797.     }
  4798.  
  4799.     str++;
  4800.   }
  4801.   *bufc = '\0';
  4802.   return buff;
  4803. }
  4804.  
  4805. char *strip_braces(line)
  4806.      char *line;
  4807. {
  4808.   /* this is a hack which just strips a level of braces. It malloc()s memory
  4809.    * which must be free()d later.
  4810.    */
  4811.  
  4812.   char *buff;
  4813.   char *tstr, *tbuf;
  4814.   char *bufc;
  4815.   char *str = line;
  4816.  
  4817.   buff = (char *) malloc(BUFFER_LEN + 1);
  4818. #ifdef MEM_CHECK
  4819.   add_check("strip_braces.buff");
  4820. #endif
  4821.   bufc = buff;
  4822.  
  4823.   while (isspace(*str))        /* eat spaces at the beginning */
  4824.     str++;
  4825.  
  4826.   switch (*str) {
  4827.   case '{':
  4828.     tstr = str++;
  4829.     tbuf = parse_to(&str, '}', 0);
  4830.     if (str == NULL) {
  4831.       str = tstr;
  4832.       safe_str(tstr, buff, &bufc);
  4833.       *bufc = '\0';
  4834.       return buff;
  4835.     } else {
  4836.       safe_str(tbuf, buff, &bufc);
  4837.       str--;
  4838.       *bufc = '\0';
  4839.       return buff;
  4840.     }
  4841.     break;            /* NOT REACHED */
  4842.   default:
  4843.     strcpy(buff, str);
  4844.     return buff;
  4845.   }
  4846. }
  4847.  
  4848. /*------------------------------------------------------------------------
  4849.  * User-defined global function handlers 
  4850.  */
  4851.  
  4852. typedef struct userfn_entry USERFN_ENTRY;
  4853.  
  4854. struct userfn_entry {
  4855.     dbref thing;
  4856.     char *name;
  4857. };
  4858.  
  4859. static USERFN_ENTRY userfn_tab[MAX_GLOBAL_FNS];
  4860.  
  4861. static int userfn_count = 0;
  4862.  
  4863. GLOBAL_FUN(fun_gfun)
  4864. {
  4865.     /* this function can never be called directly. It is called by
  4866.      * user-defined functions.
  4867.      */
  4868.  
  4869.     ATTR *attrib;
  4870.  
  4871.     attrib = atr_get(userfn_tab[fn_num].thing, userfn_tab[fn_num].name);
  4872.     do_userfn(privs, doer, userfn_tab[fn_num].thing, attrib, args, buff, 1);
  4873. }
  4874.  
  4875. void do_function(player, name, argv)
  4876.      dbref player;
  4877.      char *name;
  4878.      char *argv[];
  4879. {
  4880.     /* command of format: @function <function name>=<thing>,<attribute>
  4881.      * Adds a new user-defined function.
  4882.      */
  4883.  
  4884.     int i;
  4885.     struct ftab_entry *tabp;
  4886.     char tbuf1[BUFFER_LEN];
  4887.     char *bp = tbuf1;
  4888.     dbref thing;
  4889.     FUN *fp;
  4890.  
  4891.     /* if no arguments, just give the list of user functions, by walking
  4892.      * the function hash table, and looking up all functions marked
  4893.      * as user-defined.
  4894.      */
  4895.  
  4896.     if (!name || !*name) {
  4897.     if (userfn_count == 0) {
  4898.         notify(player, "No global user-defined functions exist.");
  4899.         return;
  4900.     }
  4901.     if (Global_Funcs(player)) {
  4902.         /* if the player is privileged, display user-def'ed functions
  4903.          * with corresponding dbref number of thing and attribute name.
  4904.          */
  4905.         notify(player,
  4906.            "Function Name                   Dbref #    Attrib");
  4907.         for (i = 0; i < FUNC_HASH_SIZE; i++) {
  4908.         for (tabp = func_hashtab[i]; tabp != NULL; tabp = tabp->next) {
  4909.             if (tabp->fp->ftype >= GLOBAL_OFFSET)
  4910.             notify(player, 
  4911.                    tprintf("%-32s %6d    %s", tabp->fp->name,
  4912.                  userfn_tab[GF_Index(tabp->fp->ftype)].thing,
  4913.                  userfn_tab[GF_Index(tabp->fp->ftype)].name));
  4914.         }
  4915.         }
  4916.     } else {
  4917.         /* just print out the list of available functions */
  4918.         safe_str("User functions:", tbuf1, &bp);
  4919.         for (i = 0; i < FUNC_HASH_SIZE; i++) {
  4920.         for (tabp = func_hashtab[i]; tabp != NULL; tabp = tabp->next) {
  4921.             if (tabp->fp->ftype >= GLOBAL_OFFSET) {
  4922.             safe_chr(' ', tbuf1, &bp);
  4923.             safe_str(tabp->fp->name, tbuf1, &bp);
  4924.             }
  4925.         }
  4926.         }
  4927.         *bp = '\0';
  4928.         notify(player, tbuf1);
  4929.     }
  4930.     return;
  4931.     }
  4932.  
  4933.     /* otherwise, we are adding a user function. There is NO deletion
  4934.      * mechanism. Only those with the Global_Funcs power may add stuff.
  4935.      * If you add a function that is already a user-defined function,
  4936.      * the old function gets over-written.
  4937.      */
  4938.   
  4939.     if (!Global_Funcs(player)) {
  4940.     notify(player, "Permission denied.");
  4941.     return;
  4942.     }
  4943.     if (userfn_count >= MAX_GLOBAL_FNS) {
  4944.     notify(player, "Function table full.");
  4945.     return;
  4946.     }
  4947.  
  4948.     if (!argv[1] || !*argv[1] || !argv[2] || !*argv[2]) {
  4949.     notify(player, "You must specify an object and an attribute.");
  4950.     return;
  4951.     }
  4952.  
  4953.     /* make sure the function name length is okay */
  4954.     if (strlen(name) > 30) {
  4955.     notify(player, "Function name too long.");
  4956.     return;
  4957.     }
  4958.  
  4959.     /* find the object. For some measure of security, the player must
  4960.      * be able to examine it.
  4961.      */
  4962.     init_match(player, argv[1], NOTYPE);
  4963.     match_everything();
  4964.     if ((thing = noisy_match_result()) == NOTHING)
  4965.     return;
  4966.     if (!Can_Examine(player, thing)) {
  4967.     notify(player, "No permission to examine object.");
  4968.     return;
  4969.     }
  4970.     
  4971.     /* we don't need to check if the attribute exists. If it doesn't,
  4972.      * it's not our problem - it's the user's responsibility to make
  4973.      * sure that the attribute exists (if it doesn't, invoking the
  4974.      * function will return a #-1 NO SUCH ATTRIBUTE error).
  4975.      * We do, however, need to make sure that the user isn't trying
  4976.      * to replace a built-in function.
  4977.      */
  4978.  
  4979.     fp = func_hash_lookup(upcasestr(name));
  4980.     if (fp->name == NULL) {
  4981.  
  4982.     /* a completely new entry. First, insert it into general hash table */
  4983.     fp = (FUN *) malloc(sizeof(FUN));
  4984.     fp->name = (char *) strdup(name);
  4985.     fp->fun = fun_gfun;
  4986.     fp->nargs = -1;
  4987.     fp->ftype = userfn_count + GLOBAL_OFFSET;
  4988.     func_hash_insert(name, (FUN *)fp);
  4989.  
  4990.     /* now add it to the user function table */
  4991.     userfn_tab[userfn_count].thing = thing;
  4992.     userfn_tab[userfn_count].name = (char *) strdup(upcasestr(argv[2]));
  4993.     userfn_count++;
  4994.     
  4995.     notify(player, "Function added.");
  4996.     return;
  4997.     } else {
  4998.  
  4999.     /* we are modifying an old entry */
  5000.     if ((fp->ftype == FN_REG) || (fp->ftype == FN_NOPARSE)) {
  5001.         notify(player, "You cannot change a built-in function.");
  5002.         return;
  5003.     }
  5004.     userfn_tab[GF_Index(fp->ftype)].thing = thing;
  5005.     free(userfn_tab[GF_Index(fp->ftype)].name);
  5006.     userfn_tab[GF_Index(fp->ftype)].name =
  5007.         (char *)strdup(upcasestr(argv[2]));
  5008.     notify(player, "Function updated.");
  5009.     }
  5010. }
  5011.